├── .gitignore ├── .gitmodules ├── .npmignore ├── LICENSE ├── Readme.md ├── app ├── benchmark.d.ts ├── benchmark.js ├── index.d.ts ├── index.js ├── soxr_resampler.d.ts ├── soxr_resampler.js ├── soxr_resampler_thread.d.ts ├── soxr_resampler_thread.js ├── soxr_resampler_worker.d.ts ├── soxr_resampler_worker.js ├── soxr_transform.d.ts ├── soxr_transform.js ├── soxr_wasm.d.ts ├── soxr_wasm.js ├── soxr_wasm.wasm ├── soxr_wasm_thread.d.ts ├── soxr_wasm_thread.js ├── soxr_wasm_thread.wasm ├── test.d.ts ├── test.js ├── test_utils.d.ts ├── test_utils.js ├── utils.d.ts └── utils.js ├── deps ├── glue.c └── soxr_emscripten.patch ├── package.json ├── resources ├── 24000hz_mono_test.pcm ├── 24000hz_test.pcm └── 44100hz_test.pcm ├── scripts ├── build_emscripten.sh └── wasm_shared_memory_transformer.js ├── src ├── benchmark.ts ├── index.ts ├── soxr_resampler.ts ├── soxr_resampler_thread.ts ├── soxr_resampler_worker.ts ├── soxr_transform.ts ├── soxr_wasm.js ├── soxr_wasm_thread.js ├── test.ts ├── test_utils.ts └── utils.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | prebuilds 3 | resources/*_output.pcm 4 | node_modules 5 | .vscode 6 | CMakeCache.txt 7 | cmake_install.cmake 8 | CMakeFiles 9 | scripts/dockcross* 10 | .envrc 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/soxr"] 2 | path = deps/soxr 3 | url = https://git.code.sf.net/p/soxr/code 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | resources/* 2 | build/* 3 | prebuilds/* 4 | .vscode 5 | .github 6 | deps 7 | scripts 8 | .envrc 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Guillaume Besson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Soxr Webassembly Resampler 2 | 3 | This lib exposes the [Soxr resampler](https://sourceforge.net/projects/soxr/) to Javascript with WebAssembly. It doesn't have any dependancy and support NodeJS or a WebContext. Typescript typings are also provided. This can be used for audio application. 4 | 5 | 6 | ## How to use 7 | 8 | ```js 9 | import SoxrResampler, {SoxrResamplerTransform, SoxrDatatype} from 'wasm-audio-resampler'; 10 | 11 | const channels = 2; // minimum is 1, no maximum 12 | const inRate = 44100; // frequency in Hz for the input chunk 13 | const outRate = 44000; // frequency in Hz for the target chunk 14 | const inputDatatype = SoxrDatatype.SOXR_INT16; // input datatype, can be 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 15 | const outputDatatype = SoxrDatatype.SOXR_FLOAT32; // output datatype, can be 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 16 | 17 | // you need a new resampler for every audio stream you want to resample 18 | // it keeps data from previous calls to improve the resampling 19 | const resampler = new SoxrResampler( 20 | channels, 21 | audioTest.inRate, 22 | audioTest.outRate, 23 | inputDatatype, 24 | outputDatatype 25 | ); 26 | 27 | await resampler.init(); 28 | 29 | const pcmData = Buffer.from(/* interleaved PCM data in signed 16bits int */); 30 | const res = resampler.processChunk(pcmData); 31 | // res is also a buffer with interleaved signed 16 bits PCM data 32 | // once there is no more data to be resampled, you need to flush the resampler to get the last data in the internal buffer: 33 | const flushedData = resampler.processChunk(null); 34 | // you can then concat these buffers: 35 | const resampled = Buffer.concat([res, flushedData]); 36 | ``` 37 | 38 | You can look at the `src/test.ts` for more information. 39 | 40 | ## Building 41 | 42 | You need NodeJS and Emscripten to build this module. First clone the soxr git submodule with `git submodule update --init --recursive` then apply the `deps/soxr_emscripten.patch` patch to fix a bug with invalid function signature (`cd deps/soxr && git apply ../soxr_emscripten.patch`). Finally use `scripts/build_emscripten.sh` to compile soxr and build the Wasm files. 43 | 44 | Test music by https://www.bensound.com 45 | -------------------------------------------------------------------------------- /app/benchmark.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /app/benchmark.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const benny_1 = __importDefault(require("benny")); 7 | const soxr_resampler_thread_1 = require("./soxr_resampler_thread"); 8 | const _1 = __importDefault(require(".")); 9 | const test_utils_1 = require("./test_utils"); 10 | const utils_1 = require("./utils"); 11 | const main = async () => { 12 | const repeatedAudioTests = [...test_utils_1.audioTests, ...test_utils_1.audioTests, ...test_utils_1.audioTests].map((audioTest) => ({ 13 | ...audioTest, 14 | resampler: new _1.default(audioTest.channels, audioTest.inRate, audioTest.outRate, utils_1.SoxrDatatype.SOXR_INT16, utils_1.SoxrDatatype.SOXR_INT16, audioTest.quality), 15 | threadResampler: new soxr_resampler_thread_1.SoxrResamplerThread(audioTest.channels, audioTest.inRate, audioTest.outRate, utils_1.SoxrDatatype.SOXR_INT16, utils_1.SoxrDatatype.SOXR_INT16, audioTest.quality), 16 | })); 17 | for (const audioTest of repeatedAudioTests) { 18 | await audioTest.resampler.init(); 19 | await audioTest.threadResampler.init(); 20 | } 21 | await benny_1.default.suite('Parallel', benny_1.default.add('without worker', async () => { 22 | for (const audioTest of repeatedAudioTests) { 23 | const res = Buffer.concat([ 24 | audioTest.resampler.processChunk(audioTest.pcmData), 25 | audioTest.resampler.processChunk(null) 26 | ]); 27 | } 28 | }), benny_1.default.add('with worker', async () => { 29 | await Promise.all(repeatedAudioTests.map(async (audioTest) => { 30 | const res = Buffer.concat([ 31 | await audioTest.threadResampler.processChunk(audioTest.pcmData), 32 | await audioTest.threadResampler.processChunk(null) 33 | ]); 34 | })); 35 | }), benny_1.default.cycle((result, summary) => { 36 | console.log(`${result.name}: ${result.details.sampleResults.reduce((a, b) => a + b, 0) / result.details.sampleResults.length}s`); 37 | }), benny_1.default.complete()); 38 | process.exit(0); 39 | }; 40 | main(); 41 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmVuY2htYXJrLmpzIiwic291cmNlUm9vdCI6Ii8iLCJzb3VyY2VzIjpbImJlbmNobWFyay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLGtEQUFzQjtBQUN0QixtRUFBOEQ7QUFDOUQseUNBQThCO0FBQzlCLDZDQUEwQztBQUMxQyxtQ0FBdUM7QUFFdkMsTUFBTSxJQUFJLEdBQUcsS0FBSyxJQUFJLEVBQUU7SUFDdEIsTUFBTSxrQkFBa0IsR0FBRyxDQUFDLEdBQUcsdUJBQVUsRUFBRSxHQUFHLHVCQUFVLEVBQUUsR0FBRyx1QkFBVSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQzNGLEdBQUcsU0FBUztRQUNaLFNBQVMsRUFBRSxJQUFJLFVBQWEsQ0FDMUIsU0FBUyxDQUFDLFFBQVEsRUFDbEIsU0FBUyxDQUFDLE1BQU0sRUFDaEIsU0FBUyxDQUFDLE9BQU8sRUFDakIsb0JBQVksQ0FBQyxVQUFVLEVBQ3ZCLG9CQUFZLENBQUMsVUFBVSxFQUN2QixTQUFTLENBQUMsT0FBTyxDQUNsQjtRQUNELGVBQWUsRUFBRSxJQUFJLDJDQUFtQixDQUN0QyxTQUFTLENBQUMsUUFBUSxFQUNsQixTQUFTLENBQUMsTUFBTSxFQUNoQixTQUFTLENBQUMsT0FBTyxFQUNqQixvQkFBWSxDQUFDLFVBQVUsRUFDdkIsb0JBQVksQ0FBQyxVQUFVLEVBQ3ZCLFNBQVMsQ0FBQyxPQUFPLENBQ2xCO0tBQ0YsQ0FBQyxDQUFDLENBQUM7SUFFSixLQUFLLE1BQU0sU0FBUyxJQUFJLGtCQUFrQixFQUFFO1FBQzFDLE1BQU0sU0FBUyxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNqQyxNQUFNLFNBQVMsQ0FBQyxlQUFlLENBQUMsSUFBSSxFQUFFLENBQUM7S0FDeEM7SUFFRCxNQUFNLGVBQUMsQ0FBQyxLQUFLLENBQUMsVUFBVSxFQUN0QixlQUFDLENBQUMsR0FBRyxDQUFDLGdCQUFnQixFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ2pDLEtBQUssTUFBTSxTQUFTLElBQUksa0JBQWtCLEVBQUU7WUFDMUMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztnQkFDeEIsU0FBUyxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQztnQkFDbkQsU0FBUyxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDO2FBQ3ZDLENBQUMsQ0FBQztTQUNKO0lBQ0gsQ0FBQyxDQUFDLEVBQ0YsZUFBQyxDQUFDLEdBQUcsQ0FBQyxhQUFhLEVBQUUsS0FBSyxJQUFJLEVBQUU7UUFDOUIsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsU0FBUyxFQUFFLEVBQUU7WUFDM0QsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztnQkFDeEIsTUFBTSxTQUFTLENBQUMsZUFBZSxDQUFDLFlBQVksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDO2dCQUMvRCxNQUFNLFNBQVMsQ0FBQyxlQUFlLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQzthQUNuRCxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ04sQ0FBQyxDQUFDLEVBQ0YsZUFBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsRUFBRTtRQUMxQixPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsTUFBTSxDQUFDLElBQUksS0FBSyxNQUFNLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEdBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUE7SUFDaEksQ0FBQyxDQUFDLEVBQ0YsZUFBQyxDQUFDLFFBQVEsRUFBRSxDQUNiLENBQUM7SUFFRixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2xCLENBQUMsQ0FBQztBQUVGLElBQUksRUFBRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGIgZnJvbSAnYmVubnknO1xuaW1wb3J0IHsgU294clJlc2FtcGxlclRocmVhZCB9IGZyb20gJy4vc294cl9yZXNhbXBsZXJfdGhyZWFkJztcbmltcG9ydCBTb3hyUmVzYW1wbGVyIGZyb20gJy4nO1xuaW1wb3J0IHsgYXVkaW9UZXN0cyB9IGZyb20gJy4vdGVzdF91dGlscyc7XG5pbXBvcnQgeyBTb3hyRGF0YXR5cGUgfSBmcm9tICcuL3V0aWxzJztcblxuY29uc3QgbWFpbiA9IGFzeW5jICgpID0+IHtcbiAgY29uc3QgcmVwZWF0ZWRBdWRpb1Rlc3RzID0gWy4uLmF1ZGlvVGVzdHMsIC4uLmF1ZGlvVGVzdHMsIC4uLmF1ZGlvVGVzdHNdLm1hcCgoYXVkaW9UZXN0KSA9PiAoe1xuICAgIC4uLmF1ZGlvVGVzdCxcbiAgICByZXNhbXBsZXI6IG5ldyBTb3hyUmVzYW1wbGVyKFxuICAgICAgYXVkaW9UZXN0LmNoYW5uZWxzLFxuICAgICAgYXVkaW9UZXN0LmluUmF0ZSxcbiAgICAgIGF1ZGlvVGVzdC5vdXRSYXRlLFxuICAgICAgU294ckRhdGF0eXBlLlNPWFJfSU5UMTYsXG4gICAgICBTb3hyRGF0YXR5cGUuU09YUl9JTlQxNixcbiAgICAgIGF1ZGlvVGVzdC5xdWFsaXR5LFxuICAgICksXG4gICAgdGhyZWFkUmVzYW1wbGVyOiBuZXcgU294clJlc2FtcGxlclRocmVhZChcbiAgICAgIGF1ZGlvVGVzdC5jaGFubmVscyxcbiAgICAgIGF1ZGlvVGVzdC5pblJhdGUsXG4gICAgICBhdWRpb1Rlc3Qub3V0UmF0ZSxcbiAgICAgIFNveHJEYXRhdHlwZS5TT1hSX0lOVDE2LFxuICAgICAgU294ckRhdGF0eXBlLlNPWFJfSU5UMTYsXG4gICAgICBhdWRpb1Rlc3QucXVhbGl0eSxcbiAgICApLFxuICB9KSk7XG5cbiAgZm9yIChjb25zdCBhdWRpb1Rlc3Qgb2YgcmVwZWF0ZWRBdWRpb1Rlc3RzKSB7XG4gICAgYXdhaXQgYXVkaW9UZXN0LnJlc2FtcGxlci5pbml0KCk7XG4gICAgYXdhaXQgYXVkaW9UZXN0LnRocmVhZFJlc2FtcGxlci5pbml0KCk7XG4gIH1cblxuICBhd2FpdCBiLnN1aXRlKCdQYXJhbGxlbCcsXG4gICAgYi5hZGQoJ3dpdGhvdXQgd29ya2VyJywgYXN5bmMgKCkgPT4ge1xuICAgICAgZm9yIChjb25zdCBhdWRpb1Rlc3Qgb2YgcmVwZWF0ZWRBdWRpb1Rlc3RzKSB7XG4gICAgICAgIGNvbnN0IHJlcyA9IEJ1ZmZlci5jb25jYXQoW1xuICAgICAgICAgIGF1ZGlvVGVzdC5yZXNhbXBsZXIucHJvY2Vzc0NodW5rKGF1ZGlvVGVzdC5wY21EYXRhKSxcbiAgICAgICAgICBhdWRpb1Rlc3QucmVzYW1wbGVyLnByb2Nlc3NDaHVuayhudWxsKVxuICAgICAgICBdKTtcbiAgICAgIH1cbiAgICB9KSxcbiAgICBiLmFkZCgnd2l0aCB3b3JrZXInLCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChyZXBlYXRlZEF1ZGlvVGVzdHMubWFwKGFzeW5jIChhdWRpb1Rlc3QpID0+IHtcbiAgICAgICAgY29uc3QgcmVzID0gQnVmZmVyLmNvbmNhdChbXG4gICAgICAgICAgYXdhaXQgYXVkaW9UZXN0LnRocmVhZFJlc2FtcGxlci5wcm9jZXNzQ2h1bmsoYXVkaW9UZXN0LnBjbURhdGEpLFxuICAgICAgICAgIGF3YWl0IGF1ZGlvVGVzdC50aHJlYWRSZXNhbXBsZXIucHJvY2Vzc0NodW5rKG51bGwpXG4gICAgICAgIF0pO1xuICAgICAgfSkpO1xuICAgIH0pLFxuICAgIGIuY3ljbGUoKHJlc3VsdCwgc3VtbWFyeSkgPT4ge1xuICAgICAgY29uc29sZS5sb2coYCR7cmVzdWx0Lm5hbWV9OiAke3Jlc3VsdC5kZXRhaWxzLnNhbXBsZVJlc3VsdHMucmVkdWNlKChhLCBiKSA9PiBhK2IsIDApIC8gcmVzdWx0LmRldGFpbHMuc2FtcGxlUmVzdWx0cy5sZW5ndGh9c2ApXG4gICAgfSksXG4gICAgYi5jb21wbGV0ZSgpLFxuICApO1xuXG4gIHByb2Nlc3MuZXhpdCgwKTtcbn07XG5cbm1haW4oKTtcblxuXG4iXX0= -------------------------------------------------------------------------------- /app/index.d.ts: -------------------------------------------------------------------------------- 1 | import SoxrResampler from './soxr_resampler'; 2 | import { SoxrResamplerTransform } from './soxr_transform'; 3 | import { SoxrDatatype, SoxrQuality } from './utils'; 4 | import { SoxrResamplerThread } from './soxr_resampler_thread'; 5 | export { SoxrResamplerTransform, SoxrDatatype, SoxrQuality, SoxrResamplerThread }; 6 | export default SoxrResampler; 7 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.SoxrResamplerThread = exports.SoxrQuality = exports.SoxrDatatype = exports.SoxrResamplerTransform = void 0; 7 | const soxr_resampler_1 = __importDefault(require("./soxr_resampler")); 8 | const soxr_transform_1 = require("./soxr_transform"); 9 | Object.defineProperty(exports, "SoxrResamplerTransform", { enumerable: true, get: function () { return soxr_transform_1.SoxrResamplerTransform; } }); 10 | const utils_1 = require("./utils"); 11 | Object.defineProperty(exports, "SoxrDatatype", { enumerable: true, get: function () { return utils_1.SoxrDatatype; } }); 12 | Object.defineProperty(exports, "SoxrQuality", { enumerable: true, get: function () { return utils_1.SoxrQuality; } }); 13 | const soxr_resampler_thread_1 = require("./soxr_resampler_thread"); 14 | Object.defineProperty(exports, "SoxrResamplerThread", { enumerable: true, get: function () { return soxr_resampler_thread_1.SoxrResamplerThread; } }); 15 | exports.default = soxr_resampler_1.default; 16 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiLyIsInNvdXJjZXMiOlsiaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsc0VBQTZDO0FBQzdDLHFEQUEwRDtBQUlqRCx1R0FKQSx1Q0FBc0IsT0FJQTtBQUgvQixtQ0FBb0Q7QUFHbkIsNkZBSHhCLG9CQUFZLE9BR3dCO0FBQUUsNEZBSHhCLG1CQUFXLE9BR3dCO0FBRjFELG1FQUE4RDtBQUVGLG9HQUZuRCwyQ0FBbUIsT0FFbUQ7QUFDL0Usa0JBQWUsd0JBQWEsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBTb3hyUmVzYW1wbGVyIGZyb20gJy4vc294cl9yZXNhbXBsZXInO1xuaW1wb3J0IHsgU294clJlc2FtcGxlclRyYW5zZm9ybSB9IGZyb20gJy4vc294cl90cmFuc2Zvcm0nO1xuaW1wb3J0IHsgU294ckRhdGF0eXBlLCBTb3hyUXVhbGl0eSB9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHsgU294clJlc2FtcGxlclRocmVhZCB9IGZyb20gJy4vc294cl9yZXNhbXBsZXJfdGhyZWFkJztcblxuZXhwb3J0IHsgU294clJlc2FtcGxlclRyYW5zZm9ybSwgU294ckRhdGF0eXBlLCBTb3hyUXVhbGl0eSwgU294clJlc2FtcGxlclRocmVhZCB9O1xuZXhwb3J0IGRlZmF1bHQgU294clJlc2FtcGxlcjtcbiJdfQ== -------------------------------------------------------------------------------- /app/soxr_resampler.d.ts: -------------------------------------------------------------------------------- 1 | import { EmscriptenModuleSoxr, SoxrDatatype, SoxrQuality } from './utils'; 2 | declare class SoxrResampler { 3 | channels: any; 4 | inRate: any; 5 | outRate: any; 6 | inputDataType: SoxrDatatype; 7 | outputDataType: SoxrDatatype; 8 | quality: SoxrQuality; 9 | _resamplerPtr: number; 10 | _inBufferPtr: number; 11 | _inBufferSize: number; 12 | _outBufferPtr: number; 13 | _outBufferSize: number; 14 | _inProcessedLenPtr: number; 15 | _outProcessLenPtr: number; 16 | soxrModule: EmscriptenModuleSoxr; 17 | /** 18 | * Create an SpeexResampler tranform stream. 19 | * @param channels Number of channels, minimum is 1, no maximum 20 | * @param inRate frequency in Hz for the input chunk 21 | * @param outRate frequency in Hz for the target chunk 22 | * @param dataType type of the input and output data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 23 | * @param quality quality of the resampling, higher means more CPU usage, number between 0 and 6 24 | */ 25 | constructor(channels: any, inRate: any, outRate: any, inputDataType?: SoxrDatatype, outputDataType?: SoxrDatatype, quality?: SoxrQuality); 26 | init: (moduleBuilder?: any, opts?: any) => Promise; 27 | /** 28 | * Returns the minimum size required for the outputBuffer from the provided input chunk 29 | * @param chunkOrChunkLength interleaved PCM data in this.inputDataType type or null if flush is requested 30 | */ 31 | outputBufferNeededSize(chunkOrChunkLength: Uint8Array | number): number; 32 | /** 33 | * Returns the delay introduced by the resampler in number of output samples per channel 34 | */ 35 | getDelay(): number; 36 | /** 37 | * Resample a chunk of audio. 38 | * @param chunk interleaved PCM data in this.inputDataType type or null if flush is requested 39 | * @param outputBuffer Uint8Array which will store the result resampled chunk in this.outputDataType type 40 | * @returns a Uint8Array which contains the resampled data in this.outputDataType type, can be a subset of outputBuffer if it was provided 41 | */ 42 | processChunk(chunk: Uint8Array, outputBuffer?: Uint8Array): Uint8Array; 43 | prepareInternalBuffers(chunkLength: number): void; 44 | processInternalBuffer(chunkLength: number): any; 45 | } 46 | export default SoxrResampler; 47 | -------------------------------------------------------------------------------- /app/soxr_resampler.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const utils_1 = require("./utils"); 7 | const soxr_wasm_1 = __importDefault(require("./soxr_wasm")); 8 | class SoxrResampler { 9 | /** 10 | * Create an SpeexResampler tranform stream. 11 | * @param channels Number of channels, minimum is 1, no maximum 12 | * @param inRate frequency in Hz for the input chunk 13 | * @param outRate frequency in Hz for the target chunk 14 | * @param dataType type of the input and output data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 15 | * @param quality quality of the resampling, higher means more CPU usage, number between 0 and 6 16 | */ 17 | constructor(channels, inRate, outRate, inputDataType = utils_1.SoxrDatatype.SOXR_FLOAT32, outputDataType = utils_1.SoxrDatatype.SOXR_FLOAT32, quality = utils_1.SoxrQuality.SOXR_HQ) { 18 | this.channels = channels; 19 | this.inRate = inRate; 20 | this.outRate = outRate; 21 | this.inputDataType = inputDataType; 22 | this.outputDataType = outputDataType; 23 | this.quality = quality; 24 | this._inBufferPtr = -1; 25 | this._inBufferSize = -1; 26 | this._outBufferPtr = -1; 27 | this._outBufferSize = -1; 28 | this._inProcessedLenPtr = -1; 29 | this._outProcessLenPtr = -1; 30 | this.init = utils_1.memoize(async (moduleBuilder = soxr_wasm_1.default, opts) => { 31 | this.soxrModule = await moduleBuilder(opts); 32 | }); 33 | } 34 | /** 35 | * Returns the minimum size required for the outputBuffer from the provided input chunk 36 | * @param chunkOrChunkLength interleaved PCM data in this.inputDataType type or null if flush is requested 37 | */ 38 | outputBufferNeededSize(chunkOrChunkLength) { 39 | const chunkLength = !chunkOrChunkLength ? 0 : typeof chunkOrChunkLength === 'number' ? chunkOrChunkLength : chunkOrChunkLength.length; 40 | const delaySize = this.getDelay() * utils_1.bytesPerDatatypeSample[this.outputDataType] * this.channels; 41 | if (!chunkOrChunkLength) { 42 | return Math.ceil(delaySize); 43 | } 44 | return Math.ceil(delaySize + ((chunkLength / utils_1.bytesPerDatatypeSample[this.inputDataType]) * this.outRate / this.inRate * utils_1.bytesPerDatatypeSample[this.outputDataType])); 45 | } 46 | /** 47 | * Returns the delay introduced by the resampler in number of output samples per channel 48 | */ 49 | getDelay() { 50 | if (!this.soxrModule) { 51 | throw new Error('You need to wait for SoxrResampler.initPromise before calling this method'); 52 | } 53 | if (!this._resamplerPtr) { 54 | return 0; 55 | } 56 | return this.soxrModule._soxr_delay(this._resamplerPtr); 57 | } 58 | /** 59 | * Resample a chunk of audio. 60 | * @param chunk interleaved PCM data in this.inputDataType type or null if flush is requested 61 | * @param outputBuffer Uint8Array which will store the result resampled chunk in this.outputDataType type 62 | * @returns a Uint8Array which contains the resampled data in this.outputDataType type, can be a subset of outputBuffer if it was provided 63 | */ 64 | processChunk(chunk, outputBuffer) { 65 | if (!this.soxrModule) { 66 | throw new Error('You need to wait for SoxrResampler.initPromise before calling this method'); 67 | } 68 | // We check that we have as many chunks for each channel and that the last chunk is full (2 bytes) 69 | if (chunk && chunk.length % (this.channels * utils_1.bytesPerDatatypeSample[this.inputDataType]) !== 0) { 70 | throw new Error(`Chunk length should be a multiple of channels * ${utils_1.bytesPerDatatypeSample[this.inputDataType]} bytes`); 71 | } 72 | if (chunk) { 73 | // Resizing the input buffer in the WASM memory space to match what we need 74 | if (this._inBufferSize < chunk.length) { 75 | if (this._inBufferPtr !== -1) { 76 | this.soxrModule._free(this._inBufferPtr); 77 | } 78 | this._inBufferPtr = this.soxrModule._malloc(chunk.length); 79 | this._inBufferSize = chunk.length; 80 | } 81 | // Resizing the output buffer in the WASM memory space to match what we need 82 | const outBufferLengthTarget = this.outputBufferNeededSize(chunk); 83 | if (this._outBufferSize < outBufferLengthTarget) { 84 | if (this._outBufferPtr !== -1) { 85 | this.soxrModule._free(this._outBufferPtr); 86 | } 87 | this._outBufferPtr = this.soxrModule._malloc(outBufferLengthTarget); 88 | this._outBufferSize = outBufferLengthTarget; 89 | } 90 | // Copying the info from the input Buffer in the WASM memory space 91 | this.soxrModule.HEAPU8.set(chunk, this._inBufferPtr); 92 | } 93 | const outSamplesPerChannelsWritten = this.processInternalBuffer(chunk ? chunk.length : 0); 94 | const outputLength = outSamplesPerChannelsWritten * this.channels * utils_1.bytesPerDatatypeSample[this.outputDataType]; 95 | if (!outputBuffer) { 96 | outputBuffer = new Uint8Array(outputLength); 97 | } 98 | if (outputBuffer.length < outputLength) { 99 | throw new Error(`Provided outputBuffer is too small: ${outputBuffer.length} < ${outputLength}`); 100 | } 101 | outputBuffer.set(this.soxrModule.HEAPU8.subarray(this._outBufferPtr, this._outBufferPtr + outSamplesPerChannelsWritten * this.channels * utils_1.bytesPerDatatypeSample[this.outputDataType])); 102 | if (outputBuffer.length !== outputLength) { 103 | return new Uint8Array(outputBuffer.buffer, outputBuffer.byteOffset, outputLength); 104 | } 105 | else { 106 | return outputBuffer; 107 | } 108 | } 109 | prepareInternalBuffers(chunkLength) { 110 | // Resizing the input buffer in the WASM memory space to match what we need 111 | if (this._inBufferSize < chunkLength) { 112 | if (this._inBufferPtr !== -1) { 113 | this.soxrModule._free(this._inBufferPtr); 114 | } 115 | this._inBufferPtr = this.soxrModule._malloc(chunkLength); 116 | this._inBufferSize = chunkLength; 117 | } 118 | // Resizing the output buffer in the WASM memory space to match what we need 119 | const outBufferLengthTarget = this.outputBufferNeededSize(chunkLength); 120 | if (this._outBufferSize < outBufferLengthTarget) { 121 | if (this._outBufferPtr !== -1) { 122 | this.soxrModule._free(this._outBufferPtr); 123 | } 124 | this._outBufferPtr = this.soxrModule._malloc(outBufferLengthTarget); 125 | this._outBufferSize = outBufferLengthTarget; 126 | } 127 | } 128 | processInternalBuffer(chunkLength) { 129 | if (!this.soxrModule) { 130 | throw new Error('You need to wait for SoxrResampler.initPromise before calling this method'); 131 | } 132 | if (!this._resamplerPtr) { 133 | const ioSpecPtr = this.soxrModule._malloc(this.soxrModule._sizeof_soxr_io_spec_t()); 134 | this.soxrModule._soxr_io_spec(ioSpecPtr, this.inputDataType, this.outputDataType); 135 | const qualitySpecPtr = this.soxrModule._malloc(this.soxrModule._sizeof_soxr_quality_spec_t()); 136 | this.soxrModule._soxr_quality_spec(qualitySpecPtr, this.quality, 0); 137 | const errPtr = this.soxrModule._malloc(4); 138 | this._resamplerPtr = this.soxrModule._soxr_create(this.inRate, this.outRate, this.channels, errPtr, ioSpecPtr, qualitySpecPtr, 0); 139 | this.soxrModule._free(ioSpecPtr); 140 | this.soxrModule._free(qualitySpecPtr); 141 | const errNum = this.soxrModule.getValue(errPtr, 'i32'); 142 | if (errNum !== 0) { 143 | const err = new Error(this.soxrModule.AsciiToString(errNum)); 144 | this.soxrModule._free(errPtr); 145 | throw err; 146 | } 147 | this._inProcessedLenPtr = this.soxrModule._malloc(Uint32Array.BYTES_PER_ELEMENT); 148 | this._outProcessLenPtr = this.soxrModule._malloc(Uint32Array.BYTES_PER_ELEMENT); 149 | } 150 | // number of samples per channel in input buffer 151 | this.soxrModule.setValue(this._inProcessedLenPtr, 0, 'i32'); 152 | // number of samples per channels available in output buffer 153 | this.soxrModule.setValue(this._outProcessLenPtr, 0, 'i32'); 154 | const errPtr = this.soxrModule._soxr_process(this._resamplerPtr, chunkLength ? this._inBufferPtr : 0, chunkLength ? chunkLength / this.channels / utils_1.bytesPerDatatypeSample[this.inputDataType] : 0, this._inProcessedLenPtr, this._outBufferPtr, this._outBufferSize / this.channels / utils_1.bytesPerDatatypeSample[this.outputDataType], this._outProcessLenPtr); 155 | if (errPtr !== 0) { 156 | throw new Error(this.soxrModule.AsciiToString(errPtr)); 157 | } 158 | const outSamplesPerChannelsWritten = this.soxrModule.getValue(this._outProcessLenPtr, 'i32'); 159 | return outSamplesPerChannelsWritten; 160 | } 161 | } 162 | exports.default = SoxrResampler; 163 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic294cl9yZXNhbXBsZXIuanMiLCJzb3VyY2VSb290IjoiLyIsInNvdXJjZXMiOlsic294cl9yZXNhbXBsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxtQ0FBMkc7QUFFM0csNERBQW1DO0FBRW5DLE1BQU0sYUFBYTtJQVlqQjs7Ozs7OztRQU9JO0lBQ0osWUFDUyxRQUFRLEVBQ1IsTUFBTSxFQUNOLE9BQU8sRUFDUCxnQkFBZ0Isb0JBQVksQ0FBQyxZQUFZLEVBQ3pDLGlCQUFpQixvQkFBWSxDQUFDLFlBQVksRUFDMUMsVUFBVSxtQkFBVyxDQUFDLE9BQU87UUFMN0IsYUFBUSxHQUFSLFFBQVEsQ0FBQTtRQUNSLFdBQU0sR0FBTixNQUFNLENBQUE7UUFDTixZQUFPLEdBQVAsT0FBTyxDQUFBO1FBQ1Asa0JBQWEsR0FBYixhQUFhLENBQTRCO1FBQ3pDLG1CQUFjLEdBQWQsY0FBYyxDQUE0QjtRQUMxQyxZQUFPLEdBQVAsT0FBTyxDQUFzQjtRQXhCdEMsaUJBQVksR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNsQixrQkFBYSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ25CLGtCQUFhLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDbkIsbUJBQWMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUVwQix1QkFBa0IsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUN4QixzQkFBaUIsR0FBRyxDQUFDLENBQUMsQ0FBQztRQXFCdkIsU0FBSSxHQUFHLGVBQU8sQ0FBQyxLQUFLLEVBQUUsYUFBYSxHQUFHLG1CQUFRLEVBQUUsSUFBVSxFQUFFLEVBQUU7WUFDNUQsSUFBSSxDQUFDLFVBQVUsR0FBRyxNQUFNLGFBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM5QyxDQUFDLENBQUMsQ0FBQTtJQUpDLENBQUM7SUFNSjs7O01BR0U7SUFDRixzQkFBc0IsQ0FBQyxrQkFBdUM7UUFDNUQsTUFBTSxXQUFXLEdBQUcsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFD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cG9ydCB7IGJ5dGVzUGVyRGF0YXR5cGVTYW1wbGUsIEVtc2NyaXB0ZW5Nb2R1bGVTb3hyLCBtZW1vaXplLCBTb3hyRGF0YXR5cGUsIFNveHJRdWFsaXR5IH0gZnJvbSAnLi91dGlscyc7XG5cbmltcG9ydCBTb3hyV2FzbSBmcm9tICcuL3NveHJfd2FzbSc7XG5cbmNsYXNzIFNveHJSZXNhbXBsZXIge1xuICBfcmVzYW1wbGVyUHRyOiBudW1iZXI7XG4gIF9pbkJ1ZmZlclB0ciA9IC0xO1xuICBfaW5CdWZmZXJTaXplID0gLTE7XG4gIF9vdXRCdWZmZXJQdHIgPSAtMTtcbiAgX291dEJ1ZmZlclNpemUgPSAtMTtcblxuICBfaW5Qcm9jZXNzZWRMZW5QdHIgPSAtMTtcbiAgX291dFByb2Nlc3NMZW5QdHIgPSAtMTtcblxuICBzb3hyTW9kdWxlOiBFbXNjcmlwdGVuTW9kdWxlU294cjtcblxuICAvKipcbiAgICAqIENyZWF0ZSBhbiBTcGVleFJlc2FtcGxlciB0cmFuZm9ybSBzdHJlYW0uXG4gICAgKiBAcGFyYW0gY2hhbm5lbHMgTnVtYmVyIG9mIGNoYW5uZWxzLCBtaW5pbXVtIGlzIDEsIG5vIG1heGltdW1cbiAgICAqIEBwYXJhbSBpblJhdGUgZnJlcXVlbmN5IGluIEh6IGZvciB0aGUgaW5wdXQgY2h1bmtcbiAgICAqIEBwYXJhbSBvdXRSYXRlIGZyZXF1ZW5jeSBpbiBIeiBmb3IgdGhlIHRhcmdldCBjaHVua1xuICAgICogQHBhcmFtIGRhdGFUeXBlIHR5cGUgb2YgdGhlIGlucHV0IGFuZCBvdXRwdXQgZGF0YSwgMCA9IEZsb2F0MzIsIDEgPSBGbG9hdDY0LCAyID0gSW50MzIsIDMgPSBJbnQxNlxuICAgICogQHBhcmFtIHF1YWxpdHkgcXVhbGl0eSBvZiB0aGUgcmVzYW1wbGluZywgaGlnaGVyIG1lYW5zIG1vcmUgQ1BVIHVzYWdlLCBudW1iZXIgYmV0d2VlbiAwIGFuZCA2XG4gICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgcHVibGljIGNoYW5uZWxzLFxuICAgIHB1YmxpYyBpblJhdGUsXG4gICAgcHVibGljIG91dFJhdGUsXG4gICAgcHVibGljIGlucHV0RGF0YVR5cGUgPSBTb3hyRGF0YXR5cGUuU09YUl9GTE9BVDMyLFxuICAgIHB1YmxpYyBvdXRwdXREYXRhVHlwZSA9IFNveHJEYXRhdHlwZS5TT1hSX0ZMT0FUMzIsXG4gICAgcHVibGljIHF1YWxpdHkgPSBTb3hyUXVhbGl0eS5TT1hSX0hRLFxuICApIHt9XG5cbiAgaW5pdCA9IG1lbW9pemUoYXN5bmMgKG1vZHVsZUJ1aWxkZXIgPSBTb3hyV2FzbSwgb3B0cz86IGFueSkgPT4ge1xuICAgIHRoaXMuc294ck1vZHVsZSA9IGF3YWl0IG1vZHVsZUJ1aWxkZXIob3B0cyk7XG4gIH0pXG5cbiAgLyoqXG4gICogUmV0dXJucyB0aGUgbWluaW11bSBzaXplIHJlcXVpcmVkIGZvciB0aGUgb3V0cHV0QnVmZmVyIGZyb20gdGhlIHByb3ZpZGVkIGlucHV0IGNodW5rXG4gICogQHBhcmFtIGNodW5rT3JDaHVua0xlbmd0aCBpbnRlcmxlYXZlZCBQQ00gZGF0YSBpbiB0aGlzLmlucHV0RGF0YVR5cGUgdHlwZSBvciBudWxsIGlmIGZsdXNoIGlzIHJlcXVlc3RlZFxuICAqL1xuICBvdXRwdXRCdWZmZXJOZWVkZWRTaXplKGNodW5rT3JDaHVua0xlbmd0aDogVWludDhBcnJheSB8IG51bWJlcikge1xuICAgIGNvbnN0IGNodW5rTGVuZ3RoID0gIWNodW5rT3JDaHVua0xlbmd0aCA/IDAgOiB0eXBlb2YgY2h1bmtPckNodW5rTGVuZ3RoID09PSAnbnVtYmVyJyA/IGNodW5rT3JDaHVua0xlbmd0aCA6IGNodW5rT3JDaHVua0xlbmd0aC5sZW5ndGg7XG4gICAgY29uc3QgZGVsYXlTaXplID0gdGhpcy5nZXREZWxheSgpICogYnl0ZXNQZXJEYXRhdHlwZVNhbXBsZVt0aGlzLm91dHB1dERhdGFUeXBlXSAqIHRoaXMuY2hhbm5lbHM7XG4gICAgaWYgKCFjaHVua09yQ2h1bmtMZW5ndGgpIHtcbiAgICAgIHJldHVybiBNYXRoLmNlaWwoZGVsYXlTaXplKTtcbiAgICB9XG4gICAgcmV0dXJuIE1hdGguY2VpbChkZWxheVNpemUgKyAoKGNodW5rTGVuZ3RoIC8gYnl0ZXNQZXJEYXRhdHlwZVNhbXBsZVt0aGlzLmlucHV0RGF0YVR5cGVdKSAqIHRoaXMub3V0UmF0ZSAvIHRoaXMuaW5SYXRlICogYnl0ZXNQZXJEYXRhdHlwZVNhbXBsZVt0aGlzLm91dHB1dERhdGFUeXBlXSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGRlbGF5IGludHJvZHVjZWQgYnkgdGhlIHJlc2FtcGxlciBpbiBudW1iZXIgb2Ygb3V0cHV0IHNhbXBsZXMgcGVyIGNoYW5uZWxcbiAgICovXG4gIGdldERlbGF5KCkge1xuICAgIGlmICghdGhpcy5zb3hyTW9kdWxlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1lvdSBuZWVkIHRvIHdhaXQgZm9yIFNveHJSZXNhbXBsZXIuaW5pdFByb21pc2UgYmVmb3JlIGNhbGxpbmcgdGhpcyBtZXRob2QnKTtcbiAgICB9XG4gICAgaWYgKCF0aGlzLl9yZXNhbXBsZXJQdHIpIHtcbiAgICAgIHJldHVybiAwXG4gICAgfVxuICAgIHJldHVybiB0aGlzLnNveHJNb2R1bGUuX3NveHJfZGVsYXkodGhpcy5fcmVzYW1wbGVyUHRyKTtcbiAgfVxuXG4gIC8qKlxuICAqIFJlc2FtcGxlIGEgY2h1bmsgb2YgYXVkaW8uXG4gICogQHBhcmFtIGNodW5rIGludGVybGVhdmVkIFBDTSBkYXRhIGluIHRoaXMuaW5wdXREYXRhVHlwZSB0eXBlIG9yIG51bGwgaWYgZmx1c2ggaXMgcmVxdWVzdGVkXG4gICogQHBhcmFtIG91dHB1dEJ1ZmZlciBVaW50OEFycmF5IHdoaWNoIHdpbGwgc3RvcmUgdGhlIHJlc3VsdCByZXNhbXBsZWQgY2h1bmsgaW4gdGhpcy5vdXRwdXREYXRhVHlwZSB0eXBlXG4gICogQHJldHVybnMgYSBVaW50OEFycmF5IHdoaWNoIGNvbnRhaW5zIHRoZSByZXNhbXBsZWQgZGF0YSBpbiB0aGlzLm91dHB1dERhdGFUeXBlIHR5cGUsIGNhbiBiZSBhIHN1YnNldCBvZiBvdXRwdXRCdWZmZXIgaWYgaXQgd2FzIHByb3ZpZGVkXG4gICovXG4gIHByb2Nlc3NDaHVuayhjaHVuazogVWludDhBcnJheSwgb3V0cHV0QnVmZmVyPzogVWludDhBcnJheSkge1xuICAgIGlmICghdGhpcy5zb3hyTW9kdWxlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1lvdSBuZWVkIHRvIHdhaXQgZm9yIFNveHJSZXNhbXBsZXIuaW5pdFByb21pc2UgYmVmb3JlIGNhbGxpbmcgdGhpcyBtZXRob2QnKTtcbiAgICB9XG4gICAgLy8gV2UgY2hlY2sgdGhhdCB3ZSBoYXZlIGFzIG1hbnkgY2h1bmtzIGZvciBlYWNoIGNoYW5uZWwgYW5kIHRoYXQgdGhlIGxhc3QgY2h1bmsgaXMgZnVsbCAoMiBieXRlcylcbiAgICBpZiAoY2h1bmsgJiYgY2h1bmsubGVuZ3RoICUgKHRoaXMuY2hhbm5lbHMgKiBieXRlc1BlckRhdGF0eXBlU2FtcGxlW3RoaXMuaW5wdXREYXRhVHlwZV0pICE9PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYENodW5rIGxlbmd0aCBzaG91bGQgYmUgYSBtdWx0aXBsZSBvZiBjaGFubmVscyAqICR7Ynl0ZXNQZXJEYXRhdHlwZVNhbXBsZVt0aGlzLmlucHV0RGF0YVR5cGVdfSBieXRlc2ApO1xuICAgIH1cblxuICAgIGlmIChjaHVuaykge1xuICAgICAgLy8gUmVzaXppbmcgdGhlIGlucHV0IGJ1ZmZlciBpbiB0aGUgV0FTTSBtZW1vcnkgc3BhY2UgdG8gbWF0Y2ggd2hhdCB3ZSBuZWVkXG4gICAgICBpZiAodGhpcy5faW5CdWZmZXJTaXplIDwgY2h1bmsubGVuZ3RoKSB7XG4gICAgICAgIGlmICh0aGlzLl9pbkJ1ZmZlclB0ciAhPT0gLTEpIHtcbiAgICAgICAgICB0aGlzLnNveHJNb2R1bGUuX2ZyZWUodGhpcy5faW5CdWZmZXJQdHIpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuX2luQnVmZmVyUHRyID0gdGhpcy5zb3hyTW9kdWxlLl9tYWxsb2MoY2h1bmsubGVuZ3RoKTtcbiAgICAgICAgdGhpcy5faW5CdWZmZXJTaXplID0gY2h1bmsubGVuZ3RoO1xuICAgICAgfVxuXG4gICAgICAvLyBSZXNpemluZyB0aGUgb3V0cHV0IGJ1ZmZlciBpbiB0aGUgV0FTTSBtZW1vcnkgc3BhY2UgdG8gbWF0Y2ggd2hhdCB3ZSBuZWVkXG4gICAgICBjb25zdCBvdXRCdWZmZXJMZW5ndGhUYXJnZXQgPSB0aGlzLm91dHB1dEJ1ZmZlck5lZWRlZFNpemUoY2h1bmspO1xuICAgICAgaWYgKHRoaXMuX291dEJ1ZmZlclNpemUgPCBvdXRCdWZmZXJMZW5ndGhUYXJnZXQpIHtcbiAgICAgICAgaWYgKHRoaXMuX291dEJ1ZmZlclB0ciAhPT0gLTEpIHtcbiAgICAgICAgICB0aGlzLnNveHJNb2R1bGUuX2ZyZWUodGhpcy5fb3V0QnVmZmVyUHRyKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLl9vdXRCdWZmZXJQdHIgPSB0aGlzLnNveHJNb2R1bGUuX21hbGxvYyhvdXRCdWZmZXJMZW5ndGhUYXJnZXQpO1xuICAgICAgICB0aGlzLl9vdXRCdWZmZXJTaXplID0gb3V0QnVmZmVyTGVuZ3RoVGFyZ2V0O1xuICAgICAgfVxuXG4gICAgICAvLyBDb3B5aW5nIHRoZSBpbmZvIGZyb20gdGhlIGlucHV0IEJ1ZmZlciBpbiB0aGUgV0FTTSBtZW1vcnkgc3BhY2VcbiAgICAgIHRoaXMuc294ck1vZHVsZS5IRUFQVTguc2V0KGNodW5rLCB0aGlzLl9pbkJ1ZmZlclB0cik7XG4gICAgfVxuXG4gICAgY29uc3Qgb3V0U2FtcGxlc1BlckNoYW5uZWxzV3JpdHRlbiA9IHRoaXMucHJvY2Vzc0ludGVybmFsQnVmZmVyKGNodW5rID8gY2h1bmsubGVuZ3RoIDogMCk7XG5cbiAgICBjb25zdCBvdXRwdXRMZW5ndGggPSBvdXRTYW1wbGVzUGVyQ2hhbm5lbHNXcml0dGVuICogdGhpcy5jaGFubmVscyAqIGJ5dGVzUGVyRGF0YXR5cGVTYW1wbGVbdGhpcy5vdXRwdXREYXRhVHlwZV07XG4gICAgaWYgKCFvdXRwdXRCdWZmZXIpIHtcbiAgICAgIG91dHB1dEJ1ZmZlciA9IG5ldyBVaW50OEFycmF5KG91dHB1dExlbmd0aCk7XG4gICAgfVxuICAgIGlmIChvdXRwdXRCdWZmZXIubGVuZ3RoIDwgb3V0cHV0TGVuZ3RoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFByb3ZpZGVkIG91dHB1dEJ1ZmZlciBpcyB0b28gc21hbGw6ICR7b3V0cHV0QnVmZmVyLmxlbmd0aH0gPCAke291dHB1dExlbmd0aH1gKTtcbiAgICB9XG4gICAgb3V0cHV0QnVmZmVyLnNldCh0aGlzLnNveHJNb2R1bGUuSEVBUFU4LnN1YmFycmF5KFxuICAgICAgdGhpcy5fb3V0QnVmZmVyUHRyLFxuICAgICAgdGhpcy5fb3V0QnVmZmVyUHRyICsgb3V0U2FtcGxlc1BlckNoYW5uZWxzV3JpdHRlbiAqIHRoaXMuY2hhbm5lbHMgKiBieXRlc1BlckRhdGF0eXBlU2FtcGxlW3RoaXMub3V0cHV0RGF0YVR5cGVdXG4gICAgKSk7XG4gICAgaWYgKG91dHB1dEJ1ZmZlci5sZW5ndGggIT09IG91dHB1dExlbmd0aCkge1xuICAgICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KG91dHB1dEJ1ZmZlci5idWZmZXIsIG91dHB1dEJ1ZmZlci5ieXRlT2Zmc2V0LCBvdXRwdXRMZW5ndGgpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gb3V0cHV0QnVmZmVyO1xuICAgIH1cbiAgfVxuXG4gIHByZXBhcmVJbnRlcm5hbEJ1ZmZlcnMoY2h1bmtMZW5ndGg6IG51bWJlcikge1xuICAgIC8vIFJlc2l6aW5nIHRoZSBpbnB1dCBidWZmZXIgaW4gdGhlIFdBU00gbWVtb3J5IHNwYWNlIHRvIG1hdGNoIHdoYXQgd2UgbmVlZFxuICAgIGlmICh0aGlzLl9pbkJ1ZmZlclNpemUgPCBjaHVua0xlbmd0aCkge1xuICAgICAgaWYgKHRoaXMuX2luQnVmZmVyUHRyICE9PSAtMSkge1xuICAgICAgICB0aGlzLnNveHJNb2R1bGUuX2ZyZWUodGhpcy5faW5CdWZmZXJQdHIpO1xuICAgICAgfVxuICAgICAgdGhpcy5faW5CdWZmZXJQdHIgPSB0aGlzLnNveHJNb2R1bGUuX21hbGxvYyhjaHVua0xlbmd0aCk7XG4gICAgICB0aGlzLl9pbkJ1ZmZlclNpemUgPSBjaHVua0xlbmd0aDtcbiAgICB9XG5cbiAgICAvLyBSZXNpemluZyB0aGUgb3V0cHV0IGJ1ZmZlciBpbiB0aGUgV0FTTSBtZW1vcnkgc3BhY2UgdG8gbWF0Y2ggd2hhdCB3ZSBuZWVkXG4gICAgY29uc3Qgb3V0QnVmZmVyTGVuZ3RoVGFyZ2V0ID0gdGhpcy5vdXRwdXRCdWZmZXJOZWVkZWRTaXplKGNodW5rTGVuZ3RoKTtcbiAgICBpZiAodGhpcy5fb3V0QnVmZmVyU2l6ZSA8IG91dEJ1ZmZlckxlbmd0aFRhcmdldCkge1xuICAgICAgaWYgKHRoaXMuX291dEJ1ZmZlclB0ciAhPT0gLTEpIHtcbiAgICAgICAgdGhpcy5zb3hyTW9kdWxlLl9mcmVlKHRoaXMuX291dEJ1ZmZlclB0cik7XG4gICAgICB9XG4gICAgICB0aGlzLl9vdXRCdWZmZXJQdHIgPSB0aGlzLnNveHJNb2R1bGUuX21hbGxvYyhvdXRCdWZmZXJMZW5ndGhUYXJnZXQpO1xuICAgICAgdGhpcy5fb3V0QnVmZmVyU2l6ZSA9IG91dEJ1ZmZlckxlbmd0aFRhcmdldDtcbiAgICB9XG4gIH1cblxuICBwcm9jZXNzSW50ZXJuYWxCdWZmZXIoY2h1bmtMZW5ndGg6IG51bWJlcikge1xuICAgIGlmICghdGhpcy5zb3hyTW9kdWxlKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1lvdSBuZWVkIHRvIHdhaXQgZm9yIFNveHJSZXNhbXBsZXIuaW5pdFByb21pc2UgYmVmb3JlIGNhbGxpbmcgdGhpcyBtZXRob2QnKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuX3Jlc2FtcGxlclB0cikge1xuICAgICAgY29uc3QgaW9TcGVjUHRyID0gdGhpcy5zb3hyTW9kdWxlLl9tYWxsb2ModGhpcy5zb3hyTW9kdWxlLl9zaXplb2Zfc294cl9pb19zcGVjX3QoKSk7XG4gICAgICB0aGlzLnNveHJNb2R1bGUuX3NveHJfaW9fc3BlYyhpb1NwZWNQdHIsIHRoaXMuaW5wdXREYXRhVHlwZSwgdGhpcy5vdXRwdXREYXRhVHlwZSk7XG4gICAgICBjb25zdCBxdWFsaXR5U3BlY1B0ciA9IHRoaXMuc294ck1vZHVsZS5fbWFsbG9jKHRoaXMuc294ck1vZHVsZS5fc2l6ZW9mX3NveHJfcXVhbGl0eV9zcGVjX3QoKSk7XG4gICAgICB0aGlzLnNveHJNb2R1bGUuX3NveHJfcXVhbGl0eV9zcGVjKHF1YWxpdHlTcGVjUHRyLCB0aGlzLnF1YWxpdHksIDApO1xuICAgICAgY29uc3QgZXJyUHRyID0gdGhpcy5zb3hyTW9kdWxlLl9tYWxsb2MoNCk7XG4gICAgICB0aGlzLl9yZXNhbXBsZXJQdHIgPSB0aGlzLnNveHJNb2R1bGUuX3NveHJfY3JlYXRlKFxuICAgICAgICB0aGlzLmluUmF0ZSxcbiAgICAgICAgdGhpcy5vdXRSYXRlLFxuICAgICAgICB0aGlzLmNoYW5uZWxzLFxuICAgICAgICBlcnJQdHIsXG4gICAgICAgIGlvU3BlY1B0cixcbiAgICAgICAgcXVhbGl0eVNwZWNQdHIsXG4gICAgICAgIDAsXG4gICAgICApO1xuICAgICAgdGhpcy5zb3hyTW9kdWxlLl9mcmVlKGlvU3BlY1B0cik7XG4gICAgICB0aGlzLnNveHJNb2R1bGUuX2ZyZWUocXVhbGl0eVNwZWNQdHIpO1xuICAgICAgY29uc3QgZXJyTnVtID0gdGhpcy5zb3hyTW9kdWxlLmdldFZhbHVlKGVyclB0ciwgJ2kzMicpO1xuICAgICAgaWYgKGVyck51bSAhPT0gMCkge1xuICAgICAgICBjb25zdCBlcnIgPSAgbmV3IEVycm9yKHRoaXMuc294ck1vZHVsZS5Bc2NpaVRvU3RyaW5nKGVyck51bSkpO1xuICAgICAgICB0aGlzLnNveHJNb2R1bGUuX2ZyZWUoZXJyUHRyKTtcbiAgICAgICAgdGhyb3cgZXJyO1xuICAgICAgfVxuICAgICAgdGhpcy5faW5Qcm9jZXNzZWRMZW5QdHIgPSB0aGlzLnNveHJNb2R1bGUuX21hbGxvYyhVaW50MzJBcnJheS5CWVRFU19QRVJfRUxFTUVOVCk7XG4gICAgICB0aGlzLl9vdXRQcm9jZXNzTGVuUHRyID0gdGhpcy5zb3hyTW9kdWxlLl9tYWxsb2MoVWludDMyQXJyYXkuQllURVNfUEVSX0VMRU1FTlQpO1xuICAgIH1cblxuICAgIC8vIG51bWJlciBvZiBzYW1wbGVzIHBlciBjaGFubmVsIGluIGlucHV0IGJ1ZmZlclxuICAgIHRoaXMuc294ck1vZHVsZS5zZXRWYWx1ZSh0aGlzLl9pblByb2Nlc3NlZExlblB0ciwgMCwgJ2kzMicpO1xuXG4gICAgLy8gbnVtYmVyIG9mIHNhbXBsZXMgcGVyIGNoYW5uZWxzIGF2YWlsYWJsZSBpbiBvdXRwdXQgYnVmZmVyXG4gICAgdGhpcy5zb3hyTW9kdWxlLnNldFZhbHVlKHRoaXMuX291dFByb2Nlc3NMZW5QdHIsIDAsICdpMzInKTtcblxuICAgIGNvbnN0IGVyclB0ciA9IHRoaXMuc294ck1vZHVsZS5fc294cl9wcm9jZXNzKFxuICAgICAgdGhpcy5fcmVzYW1wbGVyUHRyLFxuICAgICAgY2h1bmtMZW5ndGggPyB0aGlzLl9pbkJ1ZmZlclB0ciA6IDAsXG4gICAgICBjaHVua0xlbmd0aCA/IGNodW5rTGVuZ3RoIC8gdGhpcy5jaGFubmVscyAvIGJ5dGVzUGVyRGF0YXR5cGVTYW1wbGVbdGhpcy5pbnB1dERhdGFUeXBlXSA6IDAsXG4gICAgICB0aGlzLl9pblByb2Nlc3NlZExlblB0cixcbiAgICAgIHRoaXMuX291dEJ1ZmZlclB0cixcbiAgICAgIHRoaXMuX291dEJ1ZmZlclNpemUgLyB0aGlzLmNoYW5uZWxzIC8gYnl0ZXNQZXJEYXRhdHlwZVNhbXBsZVt0aGlzLm91dHB1dERhdGFUeXBlXSxcbiAgICAgIHRoaXMuX291dFByb2Nlc3NMZW5QdHIsXG4gICAgKTtcblxuICAgIGlmIChlcnJQdHIgIT09IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcih0aGlzLnNveHJNb2R1bGUuQXNjaWlUb1N0cmluZyhlcnJQdHIpKTtcbiAgICB9XG4gICAgY29uc3Qgb3V0U2FtcGxlc1BlckNoYW5uZWxzV3JpdHRlbiA9IHRoaXMuc294ck1vZHVsZS5nZXRWYWx1ZSh0aGlzLl9vdXRQcm9jZXNzTGVuUHRyLCAnaTMyJyk7XG4gICAgcmV0dXJuIG91dFNhbXBsZXNQZXJDaGFubmVsc1dyaXR0ZW47XG4gIH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgU294clJlc2FtcGxlcjtcbiJdfQ== -------------------------------------------------------------------------------- /app/soxr_resampler_thread.d.ts: -------------------------------------------------------------------------------- 1 | import { SoxrDatatype, SoxrQuality } from './utils'; 2 | export declare class SoxrResamplerThread { 3 | channels: number; 4 | inRate: number; 5 | outRate: number; 6 | inputDataType: SoxrDatatype; 7 | outputDataType: SoxrDatatype; 8 | quality: SoxrQuality; 9 | private worker; 10 | /** 11 | * Create an SpeexResampler tranform stream. 12 | * @param channels Number of channels, minimum is 1, no maximum 13 | * @param inRate frequency in Hz for the input chunk 14 | * @param outRate frequency in Hz for the target chunk 15 | * @param dataType type of the input and output data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 16 | * @param quality quality of the resampling, higher means more CPU usage, number between 0 and 6 17 | */ 18 | constructor(channels: number, inRate: number, outRate: number, inputDataType?: SoxrDatatype, outputDataType?: SoxrDatatype, quality?: SoxrQuality); 19 | init: () => Promise; 20 | /** 21 | * Returns the minimum size required for the outputBuffer from the provided input chunk 22 | * @param chunk interleaved PCM data in this.inputDataType type or null if flush is requested 23 | */ 24 | outputBufferNeededSize(chunk: Uint8Array): Promise; 25 | /** 26 | * Returns the delay introduced by the resampler in number of output samples per channel 27 | */ 28 | getDelay(): Promise; 29 | /** 30 | * Resample a chunk of audio. 31 | * @param chunk interleaved PCM data in this.inputDataType type or null if flush is requested 32 | * @param outputBuffer Uint8Array which will store the result resampled chunk in this.outputDataType type 33 | */ 34 | processChunk: (chunk: Uint8Array, outputBuffer?: Uint8Array) => Promise; 35 | destroy(): void; 36 | } 37 | -------------------------------------------------------------------------------- /app/soxr_resampler_thread.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.SoxrResamplerThread = void 0; 4 | const threads_1 = require("threads"); 5 | const utils_1 = require("./utils"); 6 | class SoxrResamplerThread { 7 | /** 8 | * Create an SpeexResampler tranform stream. 9 | * @param channels Number of channels, minimum is 1, no maximum 10 | * @param inRate frequency in Hz for the input chunk 11 | * @param outRate frequency in Hz for the target chunk 12 | * @param dataType type of the input and output data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 13 | * @param quality quality of the resampling, higher means more CPU usage, number between 0 and 6 14 | */ 15 | constructor(channels, inRate, outRate, inputDataType = utils_1.SoxrDatatype.SOXR_FLOAT32, outputDataType = utils_1.SoxrDatatype.SOXR_FLOAT32, quality = utils_1.SoxrQuality.SOXR_HQ) { 16 | this.channels = channels; 17 | this.inRate = inRate; 18 | this.outRate = outRate; 19 | this.inputDataType = inputDataType; 20 | this.outputDataType = outputDataType; 21 | this.quality = quality; 22 | this.init = utils_1.memoize(async () => { 23 | this.worker = await threads_1.spawn(new threads_1.Worker('./soxr_resampler_worker.js')); 24 | await this.worker.init(this.channels, this.inRate, this.outRate, this.inputDataType, this.outputDataType, this.quality); 25 | }); 26 | /** 27 | * Resample a chunk of audio. 28 | * @param chunk interleaved PCM data in this.inputDataType type or null if flush is requested 29 | * @param outputBuffer Uint8Array which will store the result resampled chunk in this.outputDataType type 30 | */ 31 | this.processChunk = utils_1.limitConcurrency(async (chunk, outputBuffer) => { 32 | const chunkLength = chunk ? chunk.length : 0; 33 | const { inBufferPtr, outBufferPtr, memory } = await this.worker.prepareInBuffer(chunkLength); 34 | const HEAPU8 = new Int8Array(memory); 35 | if (chunk) { 36 | HEAPU8.set(chunk, inBufferPtr); 37 | } 38 | const outSamplesPerChannelsWritten = await this.worker.processInternalBuffer(chunkLength); 39 | const outputLength = outSamplesPerChannelsWritten * this.channels * utils_1.bytesPerDatatypeSample[this.outputDataType]; 40 | if (!outputBuffer) { 41 | outputBuffer = new Uint8Array(outputLength); 42 | } 43 | if (outputBuffer.length < outputLength) { 44 | throw new Error(`Provided outputBuffer is too small: ${outputBuffer.length} < ${outputLength}`); 45 | } 46 | outputBuffer.set(HEAPU8.subarray(outBufferPtr, outBufferPtr + outSamplesPerChannelsWritten * this.channels * utils_1.bytesPerDatatypeSample[this.outputDataType])); 47 | if (outputBuffer.length !== outputLength) { 48 | return outputBuffer.subarray(0, outputLength); 49 | } 50 | else { 51 | return outputBuffer; 52 | } 53 | }); 54 | } 55 | /** 56 | * Returns the minimum size required for the outputBuffer from the provided input chunk 57 | * @param chunk interleaved PCM data in this.inputDataType type or null if flush is requested 58 | */ 59 | async outputBufferNeededSize(chunk) { 60 | return await this.worker.outputBufferNeededSize(chunk ? chunk.length : 0); 61 | } 62 | /** 63 | * Returns the delay introduced by the resampler in number of output samples per channel 64 | */ 65 | async getDelay() { 66 | return await this.worker.getDelay(); 67 | } 68 | destroy() { 69 | threads_1.Thread.terminate(this.worker); 70 | } 71 | } 72 | exports.SoxrResamplerThread = SoxrResamplerThread; 73 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic294cl9yZXNhbXBsZXJfdGhyZWFkLmpzIiwic291cmNlUm9vdCI6Ii8iLCJzb3VyY2VzIjpbInNveHJfcmVzYW1wbGVyX3RocmVhZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxQ0FBK0M7QUFFL0MsbUNBQXVHO0FBR3ZHLE1BQWEsbUJBQW1CO0lBRzlCOzs7Ozs7O1FBT0k7SUFDSixZQUNTLFFBQWdCLEVBQ2hCLE1BQWMsRUFDZCxPQUFlLEVBQ2YsZ0JBQWdCLG9CQUFZLENBQUMsWUFBWSxFQUN6QyxpQkFBaUIsb0JBQVksQ0FBQyxZQUFZLEVBQzFDLFVBQVUsbUJBQVcsQ0FBQyxPQUFPO1FBTDdCLGFBQVEsR0FBUixRQUFRLENBQVE7UUFDaEIsV0FBTSxHQUFOLE1BQU0sQ0FBUTtRQUNkLFlBQU8sR0FBUCxPQUFPLENBQVE7UUFDZixrQkFBYSxHQUFiLGFBQWEsQ0FBNEI7UUFDekMsbUJBQWMsR0FBZCxjQUFjLENBQTRCO1FBQzFDLFlBQU8sR0FBUCxPQUFPLENBQXNCO1FBR3RDLFNBQUksR0FBRyxlQUFPLENBQUMsS0FBSyxJQUFJLEVBQUU7WUFDeEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLGVBQUssQ0FBaUIsSUFBSSxnQkFBTSxDQUFDLDRCQUE0QixDQUFDLENBQUMsQ0FBQztZQUNwRixNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxhQUFhLEVBQUUsSUFBSSxDQUFDLGNBQWMsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUgsQ0FBQyxDQUFDLENBQUE7UUFpQkY7Ozs7VUFJRTtRQUNGLGlCQUFZLEdBQUcsd0JBQWdCLENBQUMsS0FBSyxFQUFFLEtBQWlCLEVBQUUsWUFBeUIsRUFBRSxFQUFFO1lBQ3JGLE1BQU0sV0FBVyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzdDLE1BQU0sRUFBRSxXQUFXLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsV0FBVyxDQUFDLENBQUM7WUFFN0YsTUFBTSxNQUFNLEdBQUcsSUFBSSxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDckMsSUFBSSxLQUFLLEVBQUU7Z0JBQ1QsTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsV0FBVyxDQUFDLENBQUM7YUFDaEM7WUFFRCxNQUFNLDRCQUE0QixHQUFHLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUMxRixNQUFNLFlBQVksR0FBRyw0QkFBNEIsR0FBRyxJQUFJLENBQUMsUUFBUSxHQUFHLDhCQUFzQixDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQztZQUNoSCxJQUFJLENBQUMsWUFBWSxFQUFFO2dCQUNqQixZQUFZLEdBQUcsSUFBSSxVQUFVLENBQUMsWUFBWSxDQUFDLENBQUM7YUFDN0M7WUFDRCxJQUFJLFlBQVksQ0FBQyxNQUFNLEdBQUcsWUFBWSxFQUFFO2dCQUN0QyxNQUFNLElBQUksS0FBSyxDQUFDLHVDQUF1QyxZQUFZLENBQUMsTUFBTSxNQUFNLFlBQVksRUFBRSxDQUFDLENBQUM7YUFDakc7WUFDRCxZQUFZLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQzlCLFlBQVksRUFDWixZQUFZLEdBQUcsNEJBQTRCLEdBQUcsSUFBSSxDQUFDLFFBQVEsR0FBRyw4QkFBc0IsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQzFHLENBQUMsQ0FBQztZQUNILElBQUksWUFBWSxDQUFDLE1BQU0sS0FBSyxZQUFZLEVBQUU7Z0JBQ3hDLE9BQU8sWUFBWSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsWUFBWSxDQUFDLENBQUM7YUFDL0M7aUJBQU07Z0JBQ0wsT0FBTyxZQUFZLENBQUM7YUFDckI7UUFDSCxDQUFDLENBQUMsQ0FBQTtJQXJEQyxDQUFDO0lBT0o7OztNQUdFO0lBQ0YsS0FBSyxDQUFDLHNCQUFzQixDQUFDLEtBQWlCO1FBQzVDLE9BQU8sTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLHNCQUFzQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDNUUsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLFFBQVE7UUFDWixPQUFPLE1BQU0sSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUN0QyxDQUFDO0lBbUNELE9BQU87UUFDTCxnQkFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDaEMsQ0FBQztDQUNGO0FBNUVELGtEQTRFQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHNwYXduLCBUaHJlYWQsIFdvcmtlciB9IGZyb20gXCJ0aHJlYWRzXCJcblxuaW1wb3J0IHsgU294ckRhdGF0eXBlLCBieXRlc1BlckRhdGF0eXBlU2FtcGxlLCBTb3hyUXVhbGl0eSwgbWVtb2l6ZSwgbGltaXRDb25jdXJyZW5jeSB9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHtleHBvc2VkfSBmcm9tICcuL3NveHJfcmVzYW1wbGVyX3dvcmtlcic7XG5cbmV4cG9ydCBjbGFzcyBTb3hyUmVzYW1wbGVyVGhyZWFkIHtcbiAgcHJpdmF0ZSB3b3JrZXI7XG5cbiAgLyoqXG4gICAgKiBDcmVhdGUgYW4gU3BlZXhSZXNhbXBsZXIgdHJhbmZvcm0gc3RyZWFtLlxuICAgICogQHBhcmFtIGNoYW5uZWxzIE51bWJlciBvZiBjaGFubmVscywgbWluaW11bSBpcyAxLCBubyBtYXhpbXVtXG4gICAgKiBAcGFyYW0gaW5SYXRlIGZyZXF1ZW5jeSBpbiBIeiBmb3IgdGhlIGlucHV0IGNodW5rXG4gICAgKiBAcGFyYW0gb3V0UmF0ZSBmcmVxdWVuY3kgaW4gSHogZm9yIHRoZSB0YXJnZXQgY2h1bmtcbiAgICAqIEBwYXJhbSBkYXRhVHlwZSB0eXBlIG9mIHRoZSBpbnB1dCBhbmQgb3V0cHV0IGRhdGEsIDAgPSBGbG9hdDMyLCAxID0gRmxvYXQ2NCwgMiA9IEludDMyLCAzID0gSW50MTZcbiAgICAqIEBwYXJhbSBxdWFsaXR5IHF1YWxpdHkgb2YgdGhlIHJlc2FtcGxpbmcsIGhpZ2hlciBtZWFucyBtb3JlIENQVSB1c2FnZSwgbnVtYmVyIGJldHdlZW4gMCBhbmQgNlxuICAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIHB1YmxpYyBjaGFubmVsczogbnVtYmVyLFxuICAgIHB1YmxpYyBpblJhdGU6IG51bWJlcixcbiAgICBwdWJsaWMgb3V0UmF0ZTogbnVtYmVyLFxuICAgIHB1YmxpYyBpbnB1dERhdGFUeXBlID0gU294ckRhdGF0eXBlLlNPWFJfRkxPQVQzMixcbiAgICBwdWJsaWMgb3V0cHV0RGF0YVR5cGUgPSBTb3hyRGF0YXR5cGUuU09YUl9GTE9BVDMyLFxuICAgIHB1YmxpYyBxdWFsaXR5ID0gU294clF1YWxpdHkuU09YUl9IUSxcbiAgKSB7fVxuXG4gIGluaXQgPSBtZW1vaXplKGFzeW5jICgpID0+e1xuICAgIHRoaXMud29ya2VyID0gYXdhaXQgc3Bhd248dHlwZW9mIGV4cG9zZWQ+KG5ldyBXb3JrZXIoJy4vc294cl9yZXNhbXBsZXJfd29ya2VyLmpzJykpO1xuICAgIGF3YWl0IHRoaXMud29ya2VyLmluaXQodGhpcy5jaGFubmVscywgdGhpcy5pblJhdGUsIHRoaXMub3V0UmF0ZSwgdGhpcy5pbnB1dERhdGFUeXBlLCB0aGlzLm91dHB1dERhdGFUeXBlLCB0aGlzLnF1YWxpdHkpO1xuICB9KVxuXG4gIC8qKlxuICAqIFJldHVybnMgdGhlIG1pbmltdW0gc2l6ZSByZXF1aXJlZCBmb3IgdGhlIG91dHB1dEJ1ZmZlciBmcm9tIHRoZSBwcm92aWRlZCBpbnB1dCBjaHVua1xuICAqIEBwYXJhbSBjaHVuayBpbnRlcmxlYXZlZCBQQ00gZGF0YSBpbiB0aGlzLmlucHV0RGF0YVR5cGUgdHlwZSBvciBudWxsIGlmIGZsdXNoIGlzIHJlcXVlc3RlZFxuICAqL1xuICBhc3luYyBvdXRwdXRCdWZmZXJOZWVkZWRTaXplKGNodW5rOiBVaW50OEFycmF5KSB7XG4gICAgcmV0dXJuIGF3YWl0IHRoaXMud29ya2VyLm91dHB1dEJ1ZmZlck5lZWRlZFNpemUoY2h1bmsgPyBjaHVuay5sZW5ndGggOiAwKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBkZWxheSBpbnRyb2R1Y2VkIGJ5IHRoZSByZXNhbXBsZXIgaW4gbnVtYmVyIG9mIG91dHB1dCBzYW1wbGVzIHBlciBjaGFubmVsXG4gICAqL1xuICBhc3luYyBnZXREZWxheSgpIHtcbiAgICByZXR1cm4gYXdhaXQgdGhpcy53b3JrZXIuZ2V0RGVsYXkoKTtcbiAgfVxuXG4gIC8qKlxuICAqIFJlc2FtcGxlIGEgY2h1bmsgb2YgYXVkaW8uXG4gICogQHBhcmFtIGNodW5rIGludGVybGVhdmVkIFBDTSBkYXRhIGluIHRoaXMuaW5wdXREYXRhVHlwZSB0eXBlIG9yIG51bGwgaWYgZmx1c2ggaXMgcmVxdWVzdGVkXG4gICogQHBhcmFtIG91dHB1dEJ1ZmZlciBVaW50OEFycmF5IHdoaWNoIHdpbGwgc3RvcmUgdGhlIHJlc3VsdCByZXNhbXBsZWQgY2h1bmsgaW4gdGhpcy5vdXRwdXREYXRhVHlwZSB0eXBlXG4gICovXG4gIHByb2Nlc3NDaHVuayA9IGxpbWl0Q29uY3VycmVuY3koYXN5bmMgKGNodW5rOiBVaW50OEFycmF5LCBvdXRwdXRCdWZmZXI/OiBVaW50OEFycmF5KSA9PiB7XG4gICAgY29uc3QgY2h1bmtMZW5ndGggPSBjaHVuayA/IGNodW5rLmxlbmd0aCA6IDA7XG4gICAgY29uc3QgeyBpbkJ1ZmZlclB0ciwgb3V0QnVmZmVyUHRyLCBtZW1vcnkgfSA9IGF3YWl0IHRoaXMud29ya2VyLnByZXBhcmVJbkJ1ZmZlcihjaHVua0xlbmd0aCk7XG5cbiAgICBjb25zdCBIRUFQVTggPSBuZXcgSW50OEFycmF5KG1lbW9yeSk7XG4gICAgaWYgKGNodW5rKSB7XG4gICAgICBIRUFQVTguc2V0KGNodW5rLCBpbkJ1ZmZlclB0cik7XG4gICAgfVxuXG4gICAgY29uc3Qgb3V0U2FtcGxlc1BlckNoYW5uZWxzV3JpdHRlbiA9IGF3YWl0IHRoaXMud29ya2VyLnByb2Nlc3NJbnRlcm5hbEJ1ZmZlcihjaHVua0xlbmd0aCk7XG4gICAgY29uc3Qgb3V0cHV0TGVuZ3RoID0gb3V0U2FtcGxlc1BlckNoYW5uZWxzV3JpdHRlbiAqIHRoaXMuY2hhbm5lbHMgKiBieXRlc1BlckRhdGF0eXBlU2FtcGxlW3RoaXMub3V0cHV0RGF0YVR5cGVdO1xuICAgIGlmICghb3V0cHV0QnVmZmVyKSB7XG4gICAgICBvdXRwdXRCdWZmZXIgPSBuZXcgVWludDhBcnJheShvdXRwdXRMZW5ndGgpO1xuICAgIH1cbiAgICBpZiAob3V0cHV0QnVmZmVyLmxlbmd0aCA8IG91dHB1dExlbmd0aCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBQcm92aWRlZCBvdXRwdXRCdWZmZXIgaXMgdG9vIHNtYWxsOiAke291dHB1dEJ1ZmZlci5sZW5ndGh9IDwgJHtvdXRwdXRMZW5ndGh9YCk7XG4gICAgfVxuICAgIG91dHB1dEJ1ZmZlci5zZXQoSEVBUFU4LnN1YmFycmF5KFxuICAgICAgb3V0QnVmZmVyUHRyLFxuICAgICAgb3V0QnVmZmVyUHRyICsgb3V0U2FtcGxlc1BlckNoYW5uZWxzV3JpdHRlbiAqIHRoaXMuY2hhbm5lbHMgKiBieXRlc1BlckRhdGF0eXBlU2FtcGxlW3RoaXMub3V0cHV0RGF0YVR5cGVdXG4gICAgKSk7XG4gICAgaWYgKG91dHB1dEJ1ZmZlci5sZW5ndGggIT09IG91dHB1dExlbmd0aCkge1xuICAgICAgcmV0dXJuIG91dHB1dEJ1ZmZlci5zdWJhcnJheSgwLCBvdXRwdXRMZW5ndGgpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gb3V0cHV0QnVmZmVyO1xuICAgIH1cbiAgfSlcblxuICBkZXN0cm95KCkge1xuICAgIFRocmVhZC50ZXJtaW5hdGUodGhpcy53b3JrZXIpO1xuICB9XG59XG4iXX0= -------------------------------------------------------------------------------- /app/soxr_resampler_worker.d.ts: -------------------------------------------------------------------------------- 1 | import { SoxrDatatype, SoxrQuality } from './utils'; 2 | export declare const exposed: { 3 | init(channels: number, inRate: number, outRate: number, inputDataType?: SoxrDatatype, outputDataType?: SoxrDatatype, quality?: SoxrQuality): Promise; 4 | prepareInBuffer(chunkLength: number): { 5 | inBufferPtr: number; 6 | outBufferPtr: number; 7 | memory: any; 8 | }; 9 | processInternalBuffer(chunkLength: number): any; 10 | outputBufferNeededSize(chunkOrChunkLength: Uint8Array | number): number; 11 | getDelay(): number; 12 | }; 13 | -------------------------------------------------------------------------------- /app/soxr_resampler_worker.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.exposed = void 0; 7 | const soxr_resampler_1 = __importDefault(require("./soxr_resampler")); 8 | const worker_1 = require("threads/worker"); 9 | const utils_1 = require("./utils"); 10 | const soxr_wasm_thread_1 = __importDefault(require("./soxr_wasm_thread")); 11 | const INITIAL_MEMORY = 64 * 1024 * 1024; 12 | const WASM_PAGE_SIZE = 65536; 13 | let resampler; 14 | exports.exposed = { 15 | async init(channels, inRate, outRate, inputDataType = utils_1.SoxrDatatype.SOXR_FLOAT32, outputDataType = utils_1.SoxrDatatype.SOXR_FLOAT32, quality = utils_1.SoxrQuality.SOXR_HQ) { 16 | resampler = new soxr_resampler_1.default(channels, inRate, outRate, inputDataType, outputDataType, quality); 17 | const wasmMemory = new WebAssembly.Memory({ 18 | initial: INITIAL_MEMORY / WASM_PAGE_SIZE, 19 | maximum: 2147483648 / WASM_PAGE_SIZE, 20 | // @ts-ignore 21 | shared: true, 22 | }); 23 | if (!(wasmMemory.buffer instanceof SharedArrayBuffer)) { 24 | throw new Error('Runtime doesn\'t have thread support, if running on NodeJS, use --experimental-wasm-threads flag'); 25 | } 26 | await resampler.init(soxr_wasm_thread_1.default, { 27 | wasmMemory, 28 | }); 29 | }, 30 | prepareInBuffer(chunkLength) { 31 | resampler.prepareInternalBuffers(chunkLength); 32 | return { 33 | inBufferPtr: resampler._inBufferPtr, 34 | outBufferPtr: resampler._outBufferPtr, 35 | // @ts-ignore 36 | memory: resampler.soxrModule.wasmMemory.buffer, 37 | }; 38 | }, 39 | processInternalBuffer(chunkLength) { 40 | return resampler.processInternalBuffer(chunkLength); 41 | }, 42 | outputBufferNeededSize(chunkOrChunkLength) { 43 | return resampler.outputBufferNeededSize(chunkOrChunkLength); 44 | }, 45 | getDelay() { 46 | return resampler.getDelay(); 47 | }, 48 | }; 49 | worker_1.expose(exports.exposed); 50 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic294cl9yZXNhbXBsZXJfd29ya2VyLmpzIiwic291cmNlUm9vdCI6Ii8iLCJzb3VyY2VzIjpbInNveHJfcmVzYW1wbGVyX3dvcmtlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxzRUFBNEM7QUFDNUMsMkNBQXVDO0FBQ3ZDLG1DQUFvRDtBQUNwRCwwRUFBa0Q7QUFFbEQsTUFBTSxjQUFjLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUFDeEMsTUFBTSxjQUFjLEdBQUcsS0FBSyxDQUFDO0FBRTdCLElBQUksU0FBd0IsQ0FBQztBQUVoQixRQUFBLE9BQU8sR0FBRztJQUNyQixLQUFLLENBQUMsSUFBSSxDQUNSLFFBQWdCLEVBQ2hCLE1BQWMsRUFDZCxPQUFlLEVBQ2YsYUFBYSxHQUFHLG9CQUFZLENBQUMsWUFBWSxFQUN6QyxjQUFjLEdBQUcsb0JBQVksQ0FBQyxZQUFZLEVBQzFDLE9BQU8sR0FBRyxtQkFBVyxDQUFDLE9BQU87UUFFN0IsU0FBUyxHQUFHLElBQUksd0JBQWEsQ0FDM0IsUUFBUSxFQUNSLE1BQU0sRUFDTixPQUFPLEVBQ1AsYUFBYSxFQUNiLGNBQWMsRUFDZCxPQUFPLENBQ1IsQ0FBQztRQUNGLE1BQU0sVUFBVSxHQUFHLElBQUksV0FBVyxDQUFDLE1BQU0sQ0FBQztZQUN4QyxPQUFPLEVBQUUsY0FBYyxHQUFHLGNBQWM7WUFDeEMsT0FBTyxFQUFFLFVBQVUsR0FBRyxjQUFjO1lBQ3BDLGFBQWE7WUFDYixNQUFNLEVBQUUsSUFBSTtTQUNiLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxDQUFDLFVBQVUsQ0FBQyxNQUFNLFlBQVksaUJBQWlCLENBQUMsRUFBRTtZQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLGtHQUFrRyxDQUFDLENBQUE7U0FDcEg7UUFDRCxNQUFNLFNBQVMsQ0FBQyxJQUFJLENBQUMsMEJBQWdCLEVBQUU7WUFDckMsVUFBVTtTQUNYLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxlQUFlLENBQUMsV0FBbUI7UUFDakMsU0FBUyxDQUFDLHNCQUFzQixDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBRTlDLE9BQU87WUFDTCxXQUFXLEVBQUUsU0FBUyxDQUFDLFlBQVk7WUFDbkMsWUFBWSxFQUFFLFNBQVMsQ0FBQyxhQUFhO1lBQ3JDLGFBQWE7WUFDYixNQUFNLEVBQUUsU0FBUyxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsTUFBTTtTQUMvQyxDQUFDO0lBQ0osQ0FBQztJQUVELHFCQUFxQixDQUFDLFdBQW1CO1FBQ3ZDLE9BQU8sU0FBUyxDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQ3RELENBQUM7SUFFRCxzQkFBc0IsQ0FBQyxrQkFBdUM7UUFDNUQsT0FBTyxTQUFTLENBQUMsc0JBQXNCLENBQUMsa0JBQWtCLENBQUMsQ0FBQztJQUM5RCxDQUFDO0lBRUQsUUFBUTtRQUNOLE9BQU8sU0FBUyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQzlCLENBQUM7Q0FDRixDQUFDO0FBRUYsZUFBTSxDQUFDLGVBQU8sQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFNveHJSZXNhbXBsZXIgZnJvbSAnLi9zb3hyX3Jlc2FtcGxlcidcbmltcG9ydCB7IGV4cG9zZSB9IGZyb20gXCJ0aHJlYWRzL3dvcmtlclwiXG5pbXBvcnQgeyBTb3hyRGF0YXR5cGUsIFNveHJRdWFsaXR5IH0gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQgU294cldhc21UaHJlYWRlZCBmcm9tICcuL3NveHJfd2FzbV90aHJlYWQnO1xuXG5jb25zdCBJTklUSUFMX01FTU9SWSA9IDY0ICogMTAyNCAqIDEwMjQ7XG5jb25zdCBXQVNNX1BBR0VfU0laRSA9IDY1NTM2O1xuXG5sZXQgcmVzYW1wbGVyOiBTb3hyUmVzYW1wbGVyO1xuXG5leHBvcnQgY29uc3QgZXhwb3NlZCA9IHtcbiAgYXN5bmMgaW5pdChcbiAgICBjaGFubmVsczogbnVtYmVyLFxuICAgIGluUmF0ZTogbnVtYmVyLFxuICAgIG91dFJhdGU6IG51bWJlcixcbiAgICBpbnB1dERhdGFUeXBlID0gU294ckRhdGF0eXBlLlNPWFJfRkxPQVQzMixcbiAgICBvdXRwdXREYXRhVHlwZSA9IFNveHJEYXRhdHlwZS5TT1hSX0ZMT0FUMzIsXG4gICAgcXVhbGl0eSA9IFNveHJRdWFsaXR5LlNPWFJfSFEsXG4gICkge1xuICAgIHJlc2FtcGxlciA9IG5ldyBTb3hyUmVzYW1wbGVyKFxuICAgICAgY2hhbm5lbHMsXG4gICAgICBpblJhdGUsXG4gICAgICBvdXRSYXRlLFxuICAgICAgaW5wdXREYXRhVHlwZSxcbiAgICAgIG91dHB1dERhdGFUeXBlLFxuICAgICAgcXVhbGl0eSxcbiAgICApO1xuICAgIGNvbnN0IHdhc21NZW1vcnkgPSBuZXcgV2ViQXNzZW1ibHkuTWVtb3J5KHtcbiAgICAgIGluaXRpYWw6IElOSVRJQUxfTUVNT1JZIC8gV0FTTV9QQUdFX1NJWkUsXG4gICAgICBtYXhpbXVtOiAyMTQ3NDgzNjQ4IC8gV0FTTV9QQUdFX1NJWkUsXG4gICAgICAvLyBAdHMtaWdub3JlXG4gICAgICBzaGFyZWQ6IHRydWUsXG4gICAgfSk7XG4gICAgaWYgKCEod2FzbU1lbW9yeS5idWZmZXIgaW5zdGFuY2VvZiBTaGFyZWRBcnJheUJ1ZmZlcikpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignUnVudGltZSBkb2VzblxcJ3QgaGF2ZSB0aHJlYWQgc3VwcG9ydCwgaWYgcnVubmluZyBvbiBOb2RlSlMsIHVzZSAtLWV4cGVyaW1lbnRhbC13YXNtLXRocmVhZHMgZmxhZycpXG4gICAgfVxuICAgIGF3YWl0IHJlc2FtcGxlci5pbml0KFNveHJXYXNtVGhyZWFkZWQsIHtcbiAgICAgIHdhc21NZW1vcnksXG4gICAgfSk7XG4gIH0sXG5cbiAgcHJlcGFyZUluQnVmZmVyKGNodW5rTGVuZ3RoOiBudW1iZXIpIHtcbiAgICByZXNhbXBsZXIucHJlcGFyZUludGVybmFsQnVmZmVycyhjaHVua0xlbmd0aCk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgaW5CdWZmZXJQdHI6IHJlc2FtcGxlci5faW5CdWZmZXJQdHIsXG4gICAgICBvdXRCdWZmZXJQdHI6IHJlc2FtcGxlci5fb3V0QnVmZmVyUHRyLFxuICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgbWVtb3J5OiByZXNhbXBsZXIuc294ck1vZHVsZS53YXNtTWVtb3J5LmJ1ZmZlcixcbiAgICB9O1xuICB9LFxuXG4gIHByb2Nlc3NJbnRlcm5hbEJ1ZmZlcihjaHVua0xlbmd0aDogbnVtYmVyKSB7XG4gICAgcmV0dXJuIHJlc2FtcGxlci5wcm9jZXNzSW50ZXJuYWxCdWZmZXIoY2h1bmtMZW5ndGgpO1xuICB9LFxuXG4gIG91dHB1dEJ1ZmZlck5lZWRlZFNpemUoY2h1bmtPckNodW5rTGVuZ3RoOiBVaW50OEFycmF5IHwgbnVtYmVyKSB7XG4gICAgcmV0dXJuIHJlc2FtcGxlci5vdXRwdXRCdWZmZXJOZWVkZWRTaXplKGNodW5rT3JDaHVua0xlbmd0aCk7XG4gIH0sXG5cbiAgZ2V0RGVsYXkoKSB7XG4gICAgcmV0dXJuIHJlc2FtcGxlci5nZXREZWxheSgpO1xuICB9LFxufTtcblxuZXhwb3NlKGV4cG9zZWQpO1xuIl19 -------------------------------------------------------------------------------- /app/soxr_transform.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import SoxrResampler from './soxr_resampler'; 3 | import { Transform } from 'stream'; 4 | import { SoxrDatatype, SoxrQuality } from './utils'; 5 | export declare class SoxrResamplerTransform extends Transform { 6 | channels: any; 7 | inRate: any; 8 | outRate: any; 9 | inputDataType: SoxrDatatype; 10 | outputDataType: SoxrDatatype; 11 | quality: SoxrQuality; 12 | resampler: SoxrResampler; 13 | _alignementBuffer: Buffer; 14 | private initPromise; 15 | /** 16 | * Create an SpeexResampler instance. 17 | * @param channels Number of channels, minimum is 1, no maximum 18 | * @param inRate frequency in Hz for the input chunk 19 | * @param outRate frequency in Hz for the target chunk 20 | * @param inputDataType type of the input data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 21 | * @param outputDataType type of the output data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 22 | * @param quality quality of the resampling, higher means more CPU usage, number between 0 and 6 23 | */ 24 | constructor(channels: any, inRate: any, outRate: any, inputDataType?: SoxrDatatype, outputDataType?: SoxrDatatype, quality?: SoxrQuality); 25 | _transform(chunk: any, encoding: any, callback: any): void; 26 | _flush(callback: any): void; 27 | } 28 | -------------------------------------------------------------------------------- /app/soxr_transform.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.SoxrResamplerTransform = void 0; 7 | const soxr_resampler_1 = __importDefault(require("./soxr_resampler")); 8 | const stream_1 = require("stream"); 9 | const utils_1 = require("./utils"); 10 | const EMPTY_BUFFER = Buffer.alloc(0); 11 | class SoxrResamplerTransform extends stream_1.Transform { 12 | /** 13 | * Create an SpeexResampler instance. 14 | * @param channels Number of channels, minimum is 1, no maximum 15 | * @param inRate frequency in Hz for the input chunk 16 | * @param outRate frequency in Hz for the target chunk 17 | * @param inputDataType type of the input data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 18 | * @param outputDataType type of the output data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 19 | * @param quality quality of the resampling, higher means more CPU usage, number between 0 and 6 20 | */ 21 | constructor(channels, inRate, outRate, inputDataType = utils_1.SoxrDatatype.SOXR_FLOAT32, outputDataType = utils_1.SoxrDatatype.SOXR_FLOAT32, quality = utils_1.SoxrQuality.SOXR_HQ) { 22 | super(); 23 | this.channels = channels; 24 | this.inRate = inRate; 25 | this.outRate = outRate; 26 | this.inputDataType = inputDataType; 27 | this.outputDataType = outputDataType; 28 | this.quality = quality; 29 | this.resampler = new soxr_resampler_1.default(channels, inRate, outRate, inputDataType, outputDataType, quality); 30 | this.initPromise = this.resampler.init(); 31 | this.initPromise.then(() => { 32 | this.initPromise = null; 33 | }); 34 | this.channels = channels; 35 | this._alignementBuffer = EMPTY_BUFFER; 36 | } 37 | _transform(chunk, encoding, callback) { 38 | let chunkToProcess = chunk; 39 | if (this._alignementBuffer.length > 0) { 40 | chunkToProcess = Buffer.concat([ 41 | this._alignementBuffer, 42 | chunk, 43 | ]); 44 | this._alignementBuffer = EMPTY_BUFFER; 45 | } 46 | // the resampler needs a buffer aligned to 16bits times the number of channels 47 | // so we keep the extraneous bytes in a buffer for next chunk 48 | const extraneousBytesCount = chunkToProcess.length % (this.channels * utils_1.bytesPerDatatypeSample[this.inputDataType]); 49 | if (extraneousBytesCount !== 0) { 50 | this._alignementBuffer = Buffer.from(chunkToProcess.slice(chunkToProcess.length - extraneousBytesCount)); 51 | chunkToProcess = chunkToProcess.slice(0, chunkToProcess.length - extraneousBytesCount); 52 | } 53 | try { 54 | if (this.initPromise) { 55 | this.initPromise.then(() => { 56 | const res = this.resampler.processChunk(chunkToProcess); 57 | callback(null, res); 58 | }).catch((e) => { 59 | callback(e); 60 | }); 61 | } 62 | else { 63 | const res = this.resampler.processChunk(chunkToProcess); 64 | callback(null, res); 65 | } 66 | } 67 | catch (e) { 68 | callback(e); 69 | } 70 | } 71 | _flush(callback) { 72 | try { 73 | const res = this.resampler.processChunk(null); 74 | callback(null, res); 75 | } 76 | catch (e) { 77 | callback(e); 78 | } 79 | } 80 | } 81 | exports.SoxrResamplerTransform = SoxrResamplerTransform; 82 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic294cl90cmFuc2Zvcm0uanMiLCJzb3VyY2VSb290IjoiLyIsInNvdXJjZXMiOlsic294cl90cmFuc2Zvcm0udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsc0VBQTZDO0FBQzdDLG1DQUFtQztBQUNuQyxtQ0FBNEU7QUFFNUUsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUVyQyxNQUFhLHNCQUF1QixTQUFRLGtCQUFTO0lBTW5EOzs7Ozs7OztRQVFJO0lBQ0osWUFDUyxRQUFRLEVBQ1IsTUFBTSxFQUNOLE9BQU8sRUFDUCxnQkFBZ0Isb0JBQVksQ0FBQyxZQUFZLEVBQ3pDLGlCQUFpQixvQkFBWSxDQUFDLFlBQVksRUFDMUMsVUFBVSxtQkFBVyxDQUFDLE9BQU87UUFFcEMsS0FBSyxFQUFFLENBQUM7UUFQRCxhQUFRLEdBQVIsUUFBUSxDQUFBO1FBQ1IsV0FBTSxHQUFOLE1BQU0sQ0FBQTtRQUNOLFlBQU8sR0FBUCxPQUFPLENBQUE7UUFDUCxrQkFBYSxHQUFiLGFBQWEsQ0FBNEI7UUFDekMsbUJBQWMsR0FBZCxjQUFjLENBQTRCO1FBQzFDLFlBQU8sR0FBUCxPQUFPLENBQXNCO1FBR3BDLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSx3QkFBYSxDQUFDLFFBQVEsRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxjQUFjLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDdEcsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3pDLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRTtZQUN6QixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztRQUMxQixDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxZQUFZLENBQUM7SUFDeEMsQ0FBQztJQUVELFVBQVUsQ0FBQyxLQUFLLEVBQUUsUUFBUSxFQUFFLFFBQVE7UUFDbEMsSUFBSSxjQUFjLEdBQVcsS0FBSyxDQUFDO1FBQ25DLElBQUksSUFBSSxDQUFDLGlCQUFpQixDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7WUFDckMsY0FBYyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7Z0JBQzdCLElBQUksQ0FBQyxpQkFBaUI7Z0JBQ3RCLEtBQUs7YUFDTixDQUFDLENBQUM7WUFDSCxJQUFJLENBQUMsaUJBQWlCLEdBQUcsWUFBWSxDQUFDO1NBQ3ZDO1FBQ0QsOEVBQThFO1FBQzlFLDZEQUE2RDtRQUM3RCxNQUFNLG9CQUFvQixHQUFHLGNBQWMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxHQUFHLDhCQUFzQixDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDO1FBQ2xILElBQUksb0JBQW9CLEtBQUssQ0FBQyxFQUFFO1lBQzlCLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLE1BQU0sR0FBRyxvQkFBb0IsQ0FBQyxDQUFDLENBQUM7WUFDekcsY0FBYyxHQUFHLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLGNBQWMsQ0FBQyxNQUFNLEdBQUcsb0JBQW9CLENBQUMsQ0FBQztTQUN4RjtRQUNELElBQUk7WUFDRixJQUFJLElBQUksQ0FBQyxXQUFXLEVBQUU7Z0JBQ3BCLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRTtvQkFDekIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFDLENBQUM7b0JBQ3hELFFBQVEsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7Z0JBQ3RCLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFO29CQUNiLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDZCxDQUFDLENBQUMsQ0FBQzthQUNKO2lCQUFNO2dCQUNMLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLGNBQWMsQ0FBQyxDQUFDO2dCQUN4RCxRQUFRLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO2FBQ3JCO1NBQ0Y7UUFBQyxPQUFPLENBQUMsRUFBRTtZQUNWLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUNiO0lBQ0gsQ0FBQztJQUVELE1BQU0sQ0FBQyxRQUFRO1FBQ2IsSUFBSTtZQUNGLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQzlDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7U0FDckI7UUFBQyxPQUFPLENBQUMsRUFBRTtZQUNWLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQztTQUNiO0lBQ0gsQ0FBQztDQUNGO0FBMUVELHdEQTBFQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBTb3hyUmVzYW1wbGVyIGZyb20gJy4vc294cl9yZXNhbXBsZXInO1xuaW1wb3J0IHsgVHJhbnNmb3JtIH0gZnJvbSAnc3RyZWFtJztcbmltcG9ydCB7IGJ5dGVzUGVyRGF0YXR5cGVTYW1wbGUsIFNveHJEYXRhdHlwZSwgU294clF1YWxpdHkgfSBmcm9tICcuL3V0aWxzJztcblxuY29uc3QgRU1QVFlfQlVGRkVSID0gQnVmZmVyLmFsbG9jKDApO1xuXG5leHBvcnQgY2xhc3MgU294clJlc2FtcGxlclRyYW5zZm9ybSBleHRlbmRzIFRyYW5zZm9ybSB7XG4gIHJlc2FtcGxlcjogU294clJlc2FtcGxlcjtcbiAgX2FsaWduZW1lbnRCdWZmZXI6IEJ1ZmZlcjtcblxuICBwcml2YXRlIGluaXRQcm9taXNlO1xuXG4gIC8qKlxuICAgICogQ3JlYXRlIGFuIFNwZWV4UmVzYW1wbGVyIGluc3RhbmNlLlxuICAgICogQHBhcmFtIGNoYW5uZWxzIE51bWJlciBvZiBjaGFubmVscywgbWluaW11bSBpcyAxLCBubyBtYXhpbXVtXG4gICAgKiBAcGFyYW0gaW5SYXRlIGZyZXF1ZW5jeSBpbiBIeiBmb3IgdGhlIGlucHV0IGNodW5rXG4gICAgKiBAcGFyYW0gb3V0UmF0ZSBmcmVxdWVuY3kgaW4gSHogZm9yIHRoZSB0YXJnZXQgY2h1bmtcbiAgICAqIEBwYXJhbSBpbnB1dERhdGFUeXBlIHR5cGUgb2YgdGhlIGlucHV0IGRhdGEsIDAgPSBGbG9hdDMyLCAxID0gRmxvYXQ2NCwgMiA9IEludDMyLCAzID0gSW50MTZcbiAgICAqIEBwYXJhbSBvdXRwdXREYXRhVHlwZSB0eXBlIG9mIHRoZSBvdXRwdXQgZGF0YSwgMCA9IEZsb2F0MzIsIDEgPSBGbG9hdDY0LCAyID0gSW50MzIsIDMgPSBJbnQxNlxuICAgICogQHBhcmFtIHF1YWxpdHkgcXVhbGl0eSBvZiB0aGUgcmVzYW1wbGluZywgaGlnaGVyIG1lYW5zIG1vcmUgQ1BVIHVzYWdlLCBudW1iZXIgYmV0d2VlbiAwIGFuZCA2XG4gICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgcHVibGljIGNoYW5uZWxzLFxuICAgIHB1YmxpYyBpblJhdGUsXG4gICAgcHVibGljIG91dFJhdGUsXG4gICAgcHVibGljIGlucHV0RGF0YVR5cGUgPSBTb3hyRGF0YXR5cGUuU09YUl9GTE9BVDMyLFxuICAgIHB1YmxpYyBvdXRwdXREYXRhVHlwZSA9IFNveHJEYXRhdHlwZS5TT1hSX0ZMT0FUMzIsXG4gICAgcHVibGljIHF1YWxpdHkgPSBTb3hyUXVhbGl0eS5TT1hSX0hRLFxuICApIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMucmVzYW1wbGVyID0gbmV3IFNveHJSZXNhbXBsZXIoY2hhbm5lbHMsIGluUmF0ZSwgb3V0UmF0ZSwgaW5wdXREYXRhVHlwZSwgb3V0cHV0RGF0YVR5cGUsIHF1YWxpdHkpO1xuICAgIHRoaXMuaW5pdFByb21pc2UgPSB0aGlzLnJlc2FtcGxlci5pbml0KCk7XG4gICAgdGhpcy5pbml0UHJvbWlzZS50aGVuKCgpID0+IHtcbiAgICAgIHRoaXMuaW5pdFByb21pc2UgPSBudWxsO1xuICAgIH0pO1xuICAgIHRoaXMuY2hhbm5lbHMgPSBjaGFubmVscztcbiAgICB0aGlzLl9hbGlnbmVtZW50QnVmZmVyID0gRU1QVFlfQlVGRkVSO1xuICB9XG5cbiAgX3RyYW5zZm9ybShjaHVuaywgZW5jb2RpbmcsIGNhbGxiYWNrKSB7XG4gICAgbGV0IGNodW5rVG9Qcm9jZXNzOiBCdWZmZXIgPSBjaHVuaztcbiAgICBpZiAodGhpcy5fYWxpZ25lbWVudEJ1ZmZlci5sZW5ndGggPiAwKSB7XG4gICAgICBjaHVua1RvUHJvY2VzcyA9IEJ1ZmZlci5jb25jYXQoW1xuICAgICAgICB0aGlzLl9hbGlnbmVtZW50QnVmZmVyLFxuICAgICAgICBjaHVuayxcbiAgICAgIF0pO1xuICAgICAgdGhpcy5fYWxpZ25lbWVudEJ1ZmZlciA9IEVNUFRZX0JVRkZFUjtcbiAgICB9XG4gICAgLy8gdGhlIHJlc2FtcGxlciBuZWVkcyBhIGJ1ZmZlciBhbGlnbmVkIHRvIDE2Yml0cyB0aW1lcyB0aGUgbnVtYmVyIG9mIGNoYW5uZWxzXG4gICAgLy8gc28gd2Uga2VlcCB0aGUgZXh0cmFuZW91cyBieXRlcyBpbiBhIGJ1ZmZlciBmb3IgbmV4dCBjaHVua1xuICAgIGNvbnN0IGV4dHJhbmVvdXNCeXRlc0NvdW50ID0gY2h1bmtUb1Byb2Nlc3MubGVuZ3RoICUgKHRoaXMuY2hhbm5lbHMgKiBieXRlc1BlckRhdGF0eXBlU2FtcGxlW3RoaXMuaW5wdXREYXRhVHlwZV0pO1xuICAgIGlmIChleHRyYW5lb3VzQnl0ZXNDb3VudCAhPT0gMCkge1xuICAgICAgdGhpcy5fYWxpZ25lbWVudEJ1ZmZlciA9IEJ1ZmZlci5mcm9tKGNodW5rVG9Qcm9jZXNzLnNsaWNlKGNodW5rVG9Qcm9jZXNzLmxlbmd0aCAtIGV4dHJhbmVvdXNCeXRlc0NvdW50KSk7XG4gICAgICBjaHVua1RvUHJvY2VzcyA9IGNodW5rVG9Qcm9jZXNzLnNsaWNlKDAsIGNodW5rVG9Qcm9jZXNzLmxlbmd0aCAtIGV4dHJhbmVvdXNCeXRlc0NvdW50KTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGlmICh0aGlzLmluaXRQcm9taXNlKSB7XG4gICAgICAgIHRoaXMuaW5pdFByb21pc2UudGhlbigoKSA9PiB7XG4gICAgICAgICAgY29uc3QgcmVzID0gdGhpcy5yZXNhbXBsZXIucHJvY2Vzc0NodW5rKGNodW5rVG9Qcm9jZXNzKTtcbiAgICAgICAgICBjYWxsYmFjayhudWxsLCByZXMpO1xuICAgICAgICB9KS5jYXRjaCgoZSkgPT4ge1xuICAgICAgICAgIGNhbGxiYWNrKGUpO1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IHJlcyA9IHRoaXMucmVzYW1wbGVyLnByb2Nlc3NDaHVuayhjaHVua1RvUHJvY2Vzcyk7XG4gICAgICAgIGNhbGxiYWNrKG51bGwsIHJlcyk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY2FsbGJhY2soZSk7XG4gICAgfVxuICB9XG5cbiAgX2ZsdXNoKGNhbGxiYWNrKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlcyA9IHRoaXMucmVzYW1wbGVyLnByb2Nlc3NDaHVuayhudWxsKTtcbiAgICAgIGNhbGxiYWNrKG51bGwsIHJlcyk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY2FsbGJhY2soZSk7XG4gICAgfVxuICB9XG59XG4iXX0= -------------------------------------------------------------------------------- /app/soxr_wasm.d.ts: -------------------------------------------------------------------------------- 1 | export default Soxr; 2 | declare function Soxr(Soxr: any): any; 3 | -------------------------------------------------------------------------------- /app/soxr_wasm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekuillaume/wasm-audio-resampler/93a19febc96d03ef98bede78434d3c0989373477/app/soxr_wasm.wasm -------------------------------------------------------------------------------- /app/soxr_wasm_thread.d.ts: -------------------------------------------------------------------------------- 1 | export default Soxr; 2 | declare function Soxr(Soxr: any): any; 3 | -------------------------------------------------------------------------------- /app/soxr_wasm_thread.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekuillaume/wasm-audio-resampler/93a19febc96d03ef98bede78434d3c0989373477/app/soxr_wasm_thread.wasm -------------------------------------------------------------------------------- /app/test.d.ts: -------------------------------------------------------------------------------- 1 | export declare const promiseBasedTest: () => Promise; 2 | export declare const parallelTest: () => Promise; 3 | -------------------------------------------------------------------------------- /app/test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); 5 | }) : (function(o, m, k, k2) { 6 | if (k2 === undefined) k2 = k; 7 | o[k2] = m[k]; 8 | })); 9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 10 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 11 | }) : function(o, v) { 12 | o["default"] = v; 13 | }); 14 | var __importStar = (this && this.__importStar) || function (mod) { 15 | if (mod && mod.__esModule) return mod; 16 | var result = {}; 17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 18 | __setModuleDefault(result, mod); 19 | return result; 20 | }; 21 | var __importDefault = (this && this.__importDefault) || function (mod) { 22 | return (mod && mod.__esModule) ? mod : { "default": mod }; 23 | }; 24 | Object.defineProperty(exports, "__esModule", { value: true }); 25 | exports.parallelTest = exports.promiseBasedTest = void 0; 26 | const fs_1 = require("fs"); 27 | // const {promisify} = require('util'); 28 | const perf_hooks_1 = require("perf_hooks"); 29 | const path_1 = __importDefault(require("path")); 30 | const test_utils_1 = require("./test_utils"); 31 | const index_1 = __importStar(require("./index")); 32 | const soxr_resampler_thread_1 = require("./soxr_resampler_thread"); 33 | const assert = (condition, message) => { 34 | if (!condition) { 35 | throw new Error(message); 36 | } 37 | }; 38 | exports.promiseBasedTest = async () => { 39 | for (const audioTest of test_utils_1.audioTests) { 40 | const resampler = new index_1.default(audioTest.channels, audioTest.inRate, audioTest.outRate, index_1.SoxrDatatype.SOXR_INT16, index_1.SoxrDatatype.SOXR_INT16, audioTest.quality); 41 | await resampler.init(); 42 | const filename = path_1.default.parse(audioTest.inFile).name; 43 | const start = perf_hooks_1.performance.now(); 44 | const res = Buffer.concat([resampler.processChunk(audioTest.pcmData), resampler.processChunk(null)]); 45 | const end = perf_hooks_1.performance.now(); 46 | // console.log(res); 47 | console.log(`Resampling file ${audioTest.inFile} with ${audioTest.channels} channel(s) from ${audioTest.inRate}Hz to ${audioTest.outRate}Hz with quality ${audioTest.quality || 4}`); 48 | console.log(`Resampled in ${Math.floor(end - start)}ms, factor ${(audioTest.pcmData.length / (audioTest.inRate / 1000) / 2 / audioTest.channels) / (end - start)}`); 49 | console.log(`Input stream: ${audioTest.pcmData.length} bytes, ${audioTest.pcmData.length / audioTest.inRate / 2 / audioTest.channels}s`); 50 | console.log(`Output stream: ${res.length} bytes, ${res.length / audioTest.outRate / 2 / audioTest.channels}s`); 51 | console.log(); 52 | const inputDuration = audioTest.pcmData.length / audioTest.inRate / 2 / audioTest.channels; 53 | const outputDuration = res.length / audioTest.outRate / 2 / audioTest.channels; 54 | assert(Math.abs(inputDuration - outputDuration) < 0.01, `Stream duration not matching target, in: ${inputDuration}s != out:${outputDuration}`); 55 | // writeFileSync(path.resolve(__dirname, `../resources/${filename}_${audioTest.outRate}_${audioTest.quality || 7}_output.pcm`), res); 56 | } 57 | }; 58 | const streamBasedTest = async () => { 59 | console.log('================='); 60 | console.log('Tranform Stream Test'); 61 | console.log('================='); 62 | for (const audioTest of test_utils_1.audioTests) { 63 | console.log(`Resampling file ${audioTest.inFile} with ${audioTest.channels} channel(s) from ${audioTest.inRate}Hz to ${audioTest.outRate}Hz with quality ${audioTest.quality || 4}`); 64 | const readFileStream = fs_1.createReadStream(audioTest.inFile); 65 | const transformStream = new index_1.SoxrResamplerTransform(audioTest.channels, audioTest.inRate, audioTest.outRate, index_1.SoxrDatatype.SOXR_INT16, index_1.SoxrDatatype.SOXR_INT16, audioTest.quality); 66 | let pcmData = Buffer.alloc(0); 67 | readFileStream.on('data', (d) => { 68 | pcmData = Buffer.concat([pcmData, d]); 69 | }); 70 | let res = Buffer.alloc(0); 71 | transformStream.on('data', (d) => { 72 | res = Buffer.concat([res, d]); 73 | }); 74 | const start = perf_hooks_1.performance.now(); 75 | readFileStream.pipe(transformStream); 76 | await new Promise((r) => transformStream.on('end', r)); 77 | const end = perf_hooks_1.performance.now(); 78 | console.log(`Resampled in ${Math.floor(end - start)}ms, factor ${(pcmData.length / (audioTest.inRate / 1000) / 2 / audioTest.channels) / (end - start)}`); 79 | console.log(`Input stream: ${pcmData.length} bytes, ${pcmData.length / audioTest.inRate / 2 / audioTest.channels}s`); 80 | console.log(`Output stream: ${res.length} bytes, ${res.length / audioTest.outRate / 2 / audioTest.channels}s`); 81 | const inputDuration = pcmData.length / audioTest.inRate / 2 / audioTest.channels; 82 | const outputDuration = res.length / audioTest.outRate / 2 / audioTest.channels; 83 | assert(Math.abs(inputDuration - outputDuration) < 0.01, `Stream duration not matching target, in: ${inputDuration}s != out:${outputDuration}`); 84 | console.log(); 85 | } 86 | }; 87 | const smallChunksTest = async () => { 88 | console.log('================='); 89 | console.log('Small chunks Test'); 90 | console.log('================='); 91 | for (const audioTest of test_utils_1.audioTests) { 92 | const chunkSize = (audioTest.inRate / 100) * 2 * audioTest.channels; // simulate 100 chunks per seconds 93 | console.log(`Resampling file ${audioTest.inFile} with ${audioTest.channels} channel(s) from ${audioTest.inRate}Hz to ${audioTest.outRate}Hz with quality ${audioTest.quality || 4}`); 94 | const resampler = new index_1.default(audioTest.channels, audioTest.inRate, audioTest.outRate, index_1.SoxrDatatype.SOXR_INT16, index_1.SoxrDatatype.SOXR_INT16, audioTest.quality); 95 | await resampler.init(); 96 | const start = perf_hooks_1.performance.now(); 97 | for (let i = 0; i * chunkSize < audioTest.pcmData.length; i++) { 98 | const chunk = audioTest.pcmData.slice(i * chunkSize, (i + 1) * chunkSize); 99 | const res = resampler.processChunk(chunk); 100 | // if (res.length !== (audioTest.outRate / 100) * 2 * audioTest.channels) { 101 | // console.log('Diff length:', res.length); 102 | // } 103 | } 104 | const end = perf_hooks_1.performance.now(); 105 | console.log(`Resampled in ${Math.floor(end - start)}ms, factor ${(audioTest.pcmData.length / (audioTest.inRate / 1000) / 2 / audioTest.channels) / (end - start)}`); 106 | console.log(); 107 | } 108 | }; 109 | const inBufferTest = async () => { 110 | console.log('================='); 111 | console.log('In buffer small chunks test'); 112 | console.log('================='); 113 | const outputBuffer = new Uint8Array(2 * 1024 * 1024); // 2MB, should be enough for this test 114 | for (const audioTest of test_utils_1.audioTests) { 115 | const chunkSize = (audioTest.inRate / 100) * 2 * audioTest.channels; // simulate 100 chunks per seconds 116 | console.log(`Resampling file ${audioTest.inFile} with ${audioTest.channels} channel(s) from ${audioTest.inRate}Hz to ${audioTest.outRate}Hz with quality ${audioTest.quality || 4}`); 117 | const resampler = new index_1.default(audioTest.channels, audioTest.inRate, audioTest.outRate, index_1.SoxrDatatype.SOXR_INT16, index_1.SoxrDatatype.SOXR_INT16, audioTest.quality); 118 | await resampler.init(); 119 | const start = perf_hooks_1.performance.now(); 120 | for (let i = 0; i * chunkSize < audioTest.pcmData.length; i++) { 121 | const chunk = audioTest.pcmData.slice(i * chunkSize, (i + 1) * chunkSize); 122 | resampler.processChunk(chunk, outputBuffer); 123 | } 124 | const end = perf_hooks_1.performance.now(); 125 | console.log(`Resampled in ${Math.floor(end - start)}ms, factor ${(audioTest.pcmData.length / (audioTest.inRate / 1000) / 2 / audioTest.channels) / (end - start)}`); 126 | console.log(); 127 | } 128 | }; 129 | const typeChangeTest = async () => { 130 | console.log('================='); 131 | console.log('Type change test'); 132 | console.log('================='); 133 | for (const audioTest of test_utils_1.audioTests) { 134 | console.log(`Resampling file ${audioTest.inFile} with ${audioTest.channels} channel(s) from ${audioTest.inRate}Hz to ${audioTest.outRate}Hz with quality ${audioTest.quality || 4}`); 135 | const resampler = new index_1.default(audioTest.channels, audioTest.inRate, audioTest.outRate, index_1.SoxrDatatype.SOXR_INT16, index_1.SoxrDatatype.SOXR_FLOAT32, audioTest.quality); 136 | await resampler.init(); 137 | const filename = path_1.default.parse(audioTest.inFile).name; 138 | const start = perf_hooks_1.performance.now(); 139 | const res = Buffer.concat([resampler.processChunk(audioTest.pcmData), resampler.processChunk(null)]); 140 | const end = perf_hooks_1.performance.now(); 141 | console.log(`Resampled in ${Math.floor(end - start)}ms, factor ${(audioTest.pcmData.length / (audioTest.inRate / 1000) / 2 / audioTest.channels) / (end - start)}`); 142 | console.log(`Input stream: ${audioTest.pcmData.length} bytes, ${audioTest.pcmData.length / audioTest.inRate / 2 / audioTest.channels}s`); 143 | console.log(`Output stream: ${res.length} bytes, ${res.length / audioTest.outRate / Float32Array.BYTES_PER_ELEMENT / audioTest.channels}s`); 144 | const inputDuration = audioTest.pcmData.length / audioTest.inRate / 2 / audioTest.channels; 145 | const outputDuration = res.length / audioTest.outRate / Float32Array.BYTES_PER_ELEMENT / audioTest.channels; 146 | assert(Math.abs(inputDuration - outputDuration) < 0.01, `Stream duration not matching target, in: ${inputDuration}s != out:${outputDuration}`); 147 | console.log(); 148 | fs_1.writeFileSync(path_1.default.resolve(__dirname, `../resources/${filename}_${audioTest.outRate}_${audioTest.quality || 7}_output.pcm`), res); 149 | } 150 | }; 151 | exports.parallelTest = async () => { 152 | console.log('================='); 153 | console.log('Parallel Test'); 154 | console.log('================='); 155 | const start = perf_hooks_1.performance.now(); 156 | const results = await Promise.all(test_utils_1.audioTests.map(async (audioTest) => { 157 | const resampler = new soxr_resampler_thread_1.SoxrResamplerThread(audioTest.channels, audioTest.inRate, audioTest.outRate, index_1.SoxrDatatype.SOXR_INT16, index_1.SoxrDatatype.SOXR_INT16, audioTest.quality); 158 | await resampler.init(); 159 | const filename = path_1.default.parse(audioTest.inFile).name; 160 | const res = Buffer.concat([ 161 | await resampler.processChunk(audioTest.pcmData), 162 | await resampler.processChunk(null) 163 | ]); 164 | const inputDuration = audioTest.pcmData.length / audioTest.inRate / 2 / audioTest.channels; 165 | const outputDuration = res.length / audioTest.outRate / 2 / audioTest.channels; 166 | assert(Math.abs(inputDuration - outputDuration) < 0.01, `Stream duration not matching target, in: ${inputDuration}s != out:${outputDuration}`); 167 | resampler.destroy(); 168 | // writeFileSync(path.resolve(__dirname, `../resources/${filename}_${audioTest.outRate}_${audioTest.quality || 7}_output.pcm`), res); 169 | return res; 170 | })); 171 | const end = perf_hooks_1.performance.now(); 172 | console.log(`Resampled all ${test_utils_1.audioTests.length} files in ${Math.floor(end - start)}ms`); 173 | // console.log(results); 174 | }; 175 | const main = async () => { 176 | await exports.promiseBasedTest(); 177 | await streamBasedTest(); 178 | await smallChunksTest(); 179 | await inBufferTest(); 180 | await typeChangeTest(); 181 | await exports.parallelTest(); 182 | }; 183 | main().catch((e) => { 184 | console.error(e); 185 | process.exit(1); 186 | }); 187 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIvIiwic291cmNlcyI6WyJ0ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSwyQkFBa0Q7QUFDbEQsdUNBQXVDO0FBQ3ZDLDJDQUF3QztBQUN4QyxnREFBd0I7QUFDeEIsNkNBQTBDO0FBRTFDLGlEQUF5RjtBQUN6RixtRUFBOEQ7QUFFOUQsTUFBTSxNQUFNLEdBQUcsQ0FBQyxTQUFTLEVBQUUsT0FBTyxFQUFFLEVBQUU7SUFDcEMsSUFBSSxDQUFDLFNBQVMsRUFBRTtRQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7S0FDMUI7QUFDSCxDQUFDLENBQUE7QUFFWSxRQUFBLGdCQUFnQixHQUFHLEtBQUssSUFBSSxFQUFFO0lBQ3pDLEtBQUssTUFBTSxTQUFTLElBQUksdUJBQVUsRUFBRTtRQUNsQyxNQUFNLFNBQVMsR0FBRyxJQUFJLGVBQWEsQ0FDakMsU0FBUyxDQUFDLFFBQVEsRUFDbEIsU0FBUyxDQUFDLE1BQU0sRUFDaEIsU0FBUyxDQUFDLE9BQU8sRUFDakIsb0JBQVksQ0FBQyxVQUFVLEVBQ3ZCLG9CQUFZLENBQUMsVUFBVSxFQUN2QixTQUFTLENBQUMsT0FBTyxDQUNsQixDQUFDO1FBQ0YsTUFBTSxTQUFTLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDdkIsTUFBTSxRQUFRLEdBQUcsY0FBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxDQUFDO1FBRW5ELE1BQU0sS0FBSyxHQUFHLHdCQUFXLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDaEMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxFQUFFLFNBQVMsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3JHLE1BQU0sR0FBRyxH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QUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDeEYsd0JBQXdCO0FBQzFCLENBQUMsQ0FBQTtBQUVELE1BQU0sSUFBSSxHQUFHLEtBQUssSUFBSSxFQUFFO0lBQ3RCLE1BQU0sd0JBQWdCLEVBQUUsQ0FBQztJQUN6QixNQUFNLGVBQWUsRUFBRSxDQUFDO0lBQ3hCLE1BQU0sZUFBZSxFQUFFLENBQUM7SUFDeEIsTUFBTSxZQUFZLEVBQUUsQ0FBQztJQUNyQixNQUFNLGNBQWMsRUFBRSxDQUFDO0lBQ3ZCLE1BQU0sb0JBQVksRUFBRSxDQUFDO0FBQ3ZCLENBQUMsQ0FBQztBQUVGLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFO0lBQ2pCLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDakIsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNsQixDQUFDLENBQUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7d3JpdGVGaWxlU3luYyxjcmVhdGVSZWFkU3RyZWFtfSBmcm9tICdmcyc7XG4vLyBjb25zdCB7cHJvbWlzaWZ5fSA9IHJlcXVpcmUoJ3V0aWwnKTtcbmltcG9ydCB7IHBlcmZvcm1hbmNlIH0gZnJvbSAncGVyZl9ob29rcydcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgYXVkaW9UZXN0cyB9IGZyb20gJy4vdGVzdF91dGlscyc7XG5cbmltcG9ydCBTb3hyUmVzYW1wbGVyLCB7U294clJlc2FtcGxlclRyYW5zZm9ybSwgU294ckRhdGF0eXBlLCBTb3hyUXVhbGl0eX0gZnJvbSAnLi9pbmRleCc7XG5pbXBvcnQgeyBTb3hyUmVzYW1wbGVyVGhyZWFkIH0gZnJvbSAnLi9zb3hyX3Jlc2FtcGxlcl90aHJlYWQnO1xuXG5jb25zdCBhc3NlcnQgPSAoY29uZGl0aW9uLCBtZXNzYWdlKSA9PiB7XG4gIGlmICghY29uZGl0aW9uKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKG1lc3NhZ2UpO1xuICB9XG59XG5cbmV4cG9ydCBjb25zdCBwcm9taXNlQmFzZWRUZXN0ID0gYXN5bmMgKCkgPT4ge1xuICBmb3IgKGNvbnN0IGF1ZGlvVGVzdCBvZiBhdWRpb1Rlc3RzKSB7XG4gICAgY29uc3QgcmVzYW1wbGVyID0gbmV3IFNveHJSZXNhbXBsZXIoXG4gICAgICBhdWRpb1Rlc3QuY2hhbm5lbHMsXG4gICAgICBhdWRpb1Rlc3QuaW5SYXRlLFxuICAgICAgYXVkaW9UZXN0Lm91dFJhdGUsXG4gICAgICBTb3hyRGF0YXR5cGUuU09YUl9JTlQxNixcbiAgICAgIFNveHJEYXRhdHlwZS5TT1hSX0lOVDE2LFxuICAgICAgYXVkaW9UZXN0LnF1YWxpdHksXG4gICAgKTtcbiAgICBhd2FpdCByZXNhbXBsZXIuaW5pdCgpO1xuICAgIGNvbnN0IGZpbGVuYW1lID0gcGF0aC5wYXJzZShhdWRpb1Rlc3QuaW5GaWxlKS5uYW1lO1xuXG4gICAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICBjb25zdCByZXMgPSBCdWZmZXIuY29uY2F0KFtyZXNhbXBsZXIucHJvY2Vzc0NodW5rKGF1ZGlvVGVzdC5wY21EYXRhKSwgcmVzYW1wbGVyLnByb2Nlc3NDaHVuayhudWxsKV0pO1xuICAgIGNvbnN0IGVuZCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgIC8vIGNvbnNvbGUubG9nKHJlcyk7XG4gICAgY29uc29sZS5sb2coYFJlc2FtcGxpbmcgZmlsZSAke2F1ZGlvVGVzdC5pbkZpbGV9IHdpdGggJHthdWRpb1Rlc3QuY2hhbm5lbHN9IGNoYW5uZWwocykgZnJvbSAke2F1ZGlvVGVzdC5pblJhdGV9SHogdG8gJHthdWRpb1Rlc3Qub3V0UmF0ZX1IeiB3aXRoIHF1YWxpdHkgJHthdWRpb1Rlc3QucXVhbGl0eSB8fCA0fWApO1xuICAgIGNvbnNvbGUubG9nKGBSZXNhbXBsZWQgaW4gJHtNYXRoLmZsb29yKGVuZCAtIHN0YXJ0KX1tcywgZmFjdG9yICR7KGF1ZGlvVGVzdC5wY21EYXRhLmxlbmd0aCAvIChhdWRpb1Rlc3QuaW5SYXRlIC8gMTAwMCkgLyAyIC8gYXVkaW9UZXN0LmNoYW5uZWxzKSAvIChlbmQgLSBzdGFydCl9YCk7XG4gICAgY29uc29sZS5sb2coYElucHV0IHN0cmVhbTogJHthdWRpb1Rlc3QucGNtRGF0YS5sZW5ndGh9IGJ5dGVzLCAke2F1ZGlvVGVzdC5wY21EYXRhLmxlbmd0aCAvIGF1ZGlvVGVzdC5pblJhdGUgLyAyIC8gYXVkaW9UZXN0LmNoYW5uZWxzfXNgKTtcbiAgICBjb25zb2xlLmxvZyhgT3V0cHV0IHN0cmVhbTogJHtyZXMubGVuZ3RofSBieXRlcywgJHtyZXMubGVuZ3RoIC8gYXVkaW9UZXN0Lm91dFJhdGUgLyAyIC8gYXVkaW9UZXN0LmNoYW5uZWxzfXNgKTtcbiAgICBjb25zb2xlLmxvZygpO1xuXG4gICAgY29uc3QgaW5wdXREdXJhdGlvbiA9IGF1ZGlvVGVzdC5wY21EYXRhLmxlbmd0aCAvIGF1ZGlvVGVzdC5pblJhdGUgLyAyIC8gYXVkaW9UZXN0LmNoYW5uZWxzO1xuICAgIGNvbnN0IG91dHB1dER1cmF0aW9uID0gcmVzLmxlbmd0aCAvIGF1ZGlvVGVzdC5vdXRSYXRlIC8gMiAvIGF1ZGlvVGVzdC5jaGFubmVscztcbiAgICBhc3NlcnQoTWF0aC5hYnMoaW5wdXREdXJhdGlvbiAtIG91dHB1dER1cmF0aW9uKSA8IDAuMDEsIGBTdHJlYW0gZHVyYXRpb24gbm90IG1hdGNoaW5nIHRhcmdldCwgaW46ICR7aW5wdXREdXJhdGlvbn1zICE9IG91dDoke291dHB1dER1cmF0aW9ufWApO1xuICAgIC8vIHdyaXRlRmlsZVN5bmMocGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgYC4uL3Jlc291cmNlcy8ke2ZpbGVuYW1lfV8ke2F1ZGlvVGVzdC5vdXRSYXRlfV8ke2F1ZGlvVGVzdC5xdWFsaXR5IHx8IDd9X291dHB1dC5wY21gKSwgcmVzKTtcbiAgfVxufVxuXG5jb25zdCBzdHJlYW1CYXNlZFRlc3QgPSBhc3luYyAoKSA9PiB7XG4gIGNvbnNvbGUubG9nKCc9PT09PT09PT09PT09PT09PScpO1xuICBjb25zb2xlLmxvZygnVHJhbmZvcm0gU3RyZWFtIFRlc3QnKTtcbiAgY29uc29sZS5sb2coJz09PT09PT09PT09PT09PT09Jyk7XG5cbiAgZm9yIChjb25zdCBhdWRpb1Rlc3Qgb2YgYXVkaW9UZXN0cykge1xuICAgIGNvbnNvbGUubG9nKGBSZXNhbXBsaW5nIGZpbGUgJHthdWRpb1Rlc3QuaW5GaWxlfSB3aXRoICR7YXVkaW9UZXN0LmNoYW5uZWxzfSBjaGFubmVsKHMpIGZyb20gJHthdWRpb1Rlc3QuaW5SYXRlfUh6IHRvICR7YXVkaW9UZXN0Lm91dFJhdGV9SHogd2l0aCBxdWFsaXR5ICR7YXVkaW9UZXN0LnF1YWxpdHkgfHwgNH1gKTtcbiAgICBjb25zdCByZWFkRmlsZVN0cmVhbSA9IGNyZWF0ZVJlYWRTdHJlYW0oYXVkaW9UZXN0LmluRmlsZSk7XG4gICAgY29uc3QgdHJhbnNmb3JtU3RyZWFtID0gbmV3IFNveHJSZXNhbXBsZXJUcmFuc2Zvcm0oXG4gICAgICBhdWRpb1Rlc3QuY2hhbm5lbHMsXG4gICAgICBhdWRpb1Rlc3QuaW5SYXRlLFxuICAgICAgYXVkaW9UZXN0Lm91dFJhdGUsXG4gICAgICBTb3hyRGF0YXR5cGUuU09YUl9JTlQxNixcbiAgICAgIFNveHJEYXRhdHlwZS5TT1hSX0lOVDE2LFxuICAgICAgYXVkaW9UZXN0LnF1YWxpdHksXG4gICAgKTtcbiAgICBsZXQgcGNtRGF0YSA9IEJ1ZmZlci5hbGxvYygwKTtcbiAgICByZWFkRmlsZVN0cmVhbS5vbignZGF0YScsIChkKSA9PiB7XG4gICAgICBwY21EYXRhID0gQnVmZmVyLmNvbmNhdChbIHBjbURhdGEsIGQgYXMgQnVmZmVyIF0pO1xuICAgIH0pO1xuICAgIGxldCByZXMgPSBCdWZmZXIuYWxsb2MoMCk7XG4gICAgdHJhbnNmb3JtU3RyZWFtLm9uKCdkYXRhJywgKGQpID0+IHtcbiAgICAgIHJlcyA9IEJ1ZmZlci5jb25jYXQoWyByZXMsIGQgYXMgQnVmZmVyIF0pO1xuICAgIH0pO1xuXG4gICAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICByZWFkRmlsZVN0cmVhbS5waXBlKHRyYW5zZm9ybVN0cmVhbSk7XG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHIpID0+IHRyYW5zZm9ybVN0cmVhbS5vbignZW5kJywgcikpO1xuICAgIGNvbnN0IGVuZCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgIGNvbnNvbGUubG9nKGBSZXNhbXBsZWQgaW4gJHtNYXRoLmZsb29yKGVuZCAtIHN0YXJ0KX1tcywgZmFjdG9yICR7KHBjbURhdGEubGVuZ3RoIC8gKGF1ZGlvVGVzdC5pblJhdGUgLyAxMDAwKSAvIDIgLyBhdWRpb1Rlc3QuY2hhbm5lbHMpIC8gKGVuZCAtIHN0YXJ0KX1gKTtcbiAgICBjb25zb2xlLmxvZyhgSW5wdXQgc3RyZWFtOiAke3BjbURhdGEubGVuZ3RofSBieXRlcywgJHtwY21EYXRhLmxlbmd0aCAvIGF1ZGlvVGVzdC5pblJhdGUgLyAyIC8gYXVkaW9UZXN0LmNoYW5uZWxzfXNgKTtcbiAgICBjb25zb2xlLmxvZyhgT3V0cHV0IHN0cmVhbTogJHtyZXMubGVuZ3RofSBieXRlcywgJHtyZXMubGVuZ3RoIC8gYXVkaW9UZXN0Lm91dFJhdGUgLyAyIC8gYXVkaW9UZXN0LmNoYW5uZWxzfXNgKTtcblxuICAgIGNvbnN0IGlucHV0RHVyYXRpb24gPSBwY21EYXRhLmxlbmd0aCAvIGF1ZGlvVGVzdC5pblJhdGUgLyAyIC8gYXVkaW9UZXN0LmNoYW5uZWxzO1xuICAgIGNvbnN0IG91dHB1dER1cmF0aW9uID0gcmVzLmxlbmd0aCAvIGF1ZGlvVGVzdC5vdXRSYXRlIC8gMiAvIGF1ZGlvVGVzdC5jaGFubmVscztcbiAgICBhc3NlcnQoTWF0aC5hYnMoaW5wdXREdXJhdGlvbiAtIG91dHB1dER1cmF0aW9uKSA8IDAuMDEsIGBTdHJlYW0gZHVyYXRpb24gbm90IG1hdGNoaW5nIHRhcmdldCwgaW46ICR7aW5wdXREdXJhdGlvbn1zICE9IG91dDoke291dHB1dER1cmF0aW9ufWApO1xuICAgIGNvbnNvbGUubG9nKCk7XG4gIH1cbn1cblxuY29uc3Qgc21hbGxDaHVua3NUZXN0ID0gYXN5bmMgKCkgPT4ge1xuICBjb25zb2xlLmxvZygnPT09PT09PT09PT09PT09PT0nKTtcbiAgY29uc29sZS5sb2coJ1NtYWxsIGNodW5rcyBUZXN0Jyk7XG4gIGNvbnNvbGUubG9nKCc9PT09PT09PT09PT09PT09PScpO1xuXG4gIGZvciAoY29uc3QgYXVkaW9UZXN0IG9mIGF1ZGlvVGVzdHMpIHtcbiAgICBjb25zdCBjaHVua1NpemUgPSAoYXVkaW9UZXN0LmluUmF0ZSAvIDEwMCkgKiAyICogYXVkaW9UZXN0LmNoYW5uZWxzOyAvLyBzaW11bGF0ZSAxMDAgY2h1bmtzIHBlciBzZWNvbmRzXG4gICAgY29uc29sZS5sb2coYFJlc2FtcGxpbmcgZmlsZSAke2F1ZGlvVGVzdC5pbkZpbGV9IHdpdGggJHthdWRpb1Rlc3QuY2hhbm5lbHN9IGNoYW5uZWwocykgZnJvbSAke2F1ZGlvVGVzdC5pblJhdGV9SHogdG8gJHthdWRpb1Rlc3Qub3V0UmF0ZX1IeiB3aXRoIHF1YWxpdHkgJHthdWRpb1Rlc3QucXVhbGl0eSB8fCA0fWApO1xuICAgIGNvbnN0IHJlc2FtcGxlciA9IG5ldyBTb3hyUmVzYW1wbGVyKFxuICAgICAgYXVkaW9UZXN0LmNoYW5uZWxzLFxuICAgICAgYXVkaW9UZXN0LmluUmF0ZSxcbiAgICAgIGF1ZGlvVGVzdC5vdXRSYXRlLFxuICAgICAgU294ckRhdGF0eXBlLlNPWFJfSU5UMTYsXG4gICAgICBTb3hyRGF0YXR5cGUuU09YUl9JTlQxNixcbiAgICAgIGF1ZGlvVGVzdC5xdWFsaXR5LFxuICAgICk7XG4gICAgYXdhaXQgcmVzYW1wbGVyLmluaXQoKTtcblxuICAgIGNvbnN0IHN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgKiBjaHVua1NpemUgPCBhdWRpb1Rlc3QucGNtRGF0YS5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgY2h1bmsgPSBhdWRpb1Rlc3QucGNtRGF0YS5zbGljZShpICogY2h1bmtTaXplLCAoaSArIDEpICogY2h1bmtTaXplKTtcbiAgICAgIGNvbnN0IHJlcyA9IHJlc2FtcGxlci5wcm9jZXNzQ2h1bmsoY2h1bmspO1xuICAgICAgLy8gaWYgKHJlcy5sZW5ndGggIT09IChhdWRpb1Rlc3Qub3V0UmF0ZSAvIDEwMCkgKiAyICogYXVkaW9UZXN0LmNoYW5uZWxzKSB7XG4gICAgICAvLyAgIGNvbnNvbGUubG9nKCdEaWZmIGxlbmd0aDonLCByZXMubGVuZ3RoKTtcbiAgICAgIC8vIH1cbiAgICB9XG4gICAgY29uc3QgZW5kID0gcGVyZm9ybWFuY2Uubm93KCk7XG5cbiAgICBjb25zb2xlLmxvZyhgUmVzYW1wbGVkIGluICR7TWF0aC5mbG9vcihlbmQgLSBzdGFydCl9bXMsIGZhY3RvciAkeyhhdWRpb1Rlc3QucGNtRGF0YS5sZW5ndGggLyAoYXVkaW9UZXN0LmluUmF0ZSAvIDEwMDApIC8gMiAvIGF1ZGlvVGVzdC5jaGFubmVscykgLyAoZW5kIC0gc3RhcnQpfWApO1xuXG4gICAgY29uc29sZS5sb2coKTtcbiAgfVxufVxuXG5jb25zdCBpbkJ1ZmZlclRlc3QgPSBhc3luYyAoKSA9PiB7XG4gIGNvbnNvbGUubG9nKCc9PT09PT09PT09PT09PT09PScpO1xuICBjb25zb2xlLmxvZygnSW4gYnVmZmVyIHNtYWxsIGNodW5rcyB0ZXN0Jyk7XG4gIGNvbnNvbGUubG9nKCc9PT09PT09PT09PT09PT09PScpO1xuXG4gIGNvbnN0IG91dHB1dEJ1ZmZlciA9IG5ldyBVaW50OEFycmF5KDIgKiAxMDI0ICogMTAyNCk7IC8vIDJNQiwgc2hvdWxkIGJlIGVub3VnaCBmb3IgdGhpcyB0ZXN0XG5cbiAgZm9yIChjb25zdCBhdWRpb1Rlc3Qgb2YgYXVkaW9UZXN0cykge1xuICAgIGNvbnN0IGNodW5rU2l6ZSA9IChhdWRpb1Rlc3QuaW5SYXRlIC8gMTAwKSAqIDIgKiBhdWRpb1Rlc3QuY2hhbm5lbHM7IC8vIHNpbXVsYXRlIDEwMCBjaHVua3MgcGVyIHNlY29uZHNcbiAgICBjb25zb2xlLmxvZyhgUmVzYW1wbGluZyBmaWxlICR7YXVkaW9UZXN0LmluRmlsZX0gd2l0aCAke2F1ZGlvVGVzdC5jaGFubmVsc30gY2hhbm5lbChzKSBmcm9tICR7YXVkaW9UZXN0LmluUmF0ZX1IeiB0byAke2F1ZGlvVGVzdC5vdXRSYXRlfUh6IHdpdGggcXVhbGl0eSAke2F1ZGlvVGVzdC5xdWFsaXR5IHx8IDR9YCk7XG4gICAgY29uc3QgcmVzYW1wbGVyID0gbmV3IFNveHJSZXNhbXBsZXIoXG4gICAgICBhdWRpb1Rlc3QuY2hhbm5lbHMsXG4gICAgICBhdWRpb1Rlc3QuaW5SYXRlLFxuICAgICAgYXVkaW9UZXN0Lm91dFJhdGUsXG4gICAgICBTb3hyRGF0YXR5cGUuU09YUl9JTlQxNixcbiAgICAgIFNveHJEYXRhdHlwZS5TT1hSX0lOVDE2LFxuICAgICAgYXVkaW9UZXN0LnF1YWxpdHksXG4gICAgKTtcbiAgICBhd2FpdCByZXNhbXBsZXIuaW5pdCgpO1xuXG4gICAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSAqIGNodW5rU2l6ZSA8IGF1ZGlvVGVzdC5wY21EYXRhLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBjaHVuayA9IGF1ZGlvVGVzdC5wY21EYXRhLnNsaWNlKGkgKiBjaHVua1NpemUsIChpICsgMSkgKiBjaHVua1NpemUpO1xuICAgICAgcmVzYW1wbGVyLnByb2Nlc3NDaHVuayhjaHVuaywgb3V0cHV0QnVmZmVyKTtcbiAgICB9XG4gICAgY29uc3QgZW5kID0gcGVyZm9ybWFuY2Uubm93KCk7XG5cbiAgICBjb25zb2xlLmxvZyhgUmVzYW1wbGVkIGluICR7TWF0aC5mbG9vcihlbmQgLSBzdGFydCl9bXMsIGZhY3RvciAkeyhhdWRpb1Rlc3QucGNtRGF0YS5sZW5ndGggLyAoYXVkaW9UZXN0LmluUmF0ZSAvIDEwMDApIC8gMiAvIGF1ZGlvVGVzdC5jaGFubmVscykgLyAoZW5kIC0gc3RhcnQpfWApO1xuXG4gICAgY29uc29sZS5sb2coKTtcbiAgfVxufVxuXG5jb25zdCB0eXBlQ2hhbmdlVGVzdCA9IGFzeW5jICgpID0+IHtcbiAgY29uc29sZS5sb2coJz09PT09PT09PT09PT09PT09Jyk7XG4gIGNvbnNvbGUubG9nKCdUeXBlIGNoYW5nZSB0ZXN0Jyk7XG4gIGNvbnNvbGUubG9nKCc9PT09PT09PT09PT09PT09PScpO1xuXG4gIGZvciAoY29uc3QgYXVkaW9UZXN0IG9mIGF1ZGlvVGVzdHMpIHtcbiAgICBjb25zb2xlLmxvZyhgUmVzYW1wbGluZyBmaWxlICR7YXVkaW9UZXN0LmluRmlsZX0gd2l0aCAke2F1ZGlvVGVzdC5jaGFubmVsc30gY2hhbm5lbChzKSBmcm9tICR7YXVkaW9UZXN0LmluUmF0ZX1IeiB0byAke2F1ZGlvVGVzdC5vdXRSYXRlfUh6IHdpdGggcXVhbGl0eSAke2F1ZGlvVGVzdC5xdWFsaXR5IHx8IDR9YCk7XG4gICAgY29uc3QgcmVzYW1wbGVyID0gbmV3IFNveHJSZXNhbXBsZXIoXG4gICAgICBhdWRpb1Rlc3QuY2hhbm5lbHMsXG4gICAgICBhdWRpb1Rlc3QuaW5SYXRlLFxuICAgICAgYXVkaW9UZXN0Lm91dFJhdGUsXG4gICAgICBTb3hyRGF0YXR5cGUuU09YUl9JTlQxNixcbiAgICAgIFNveHJEYXRhdHlwZS5TT1hSX0ZMT0FUMzIsXG4gICAgICBhdWRpb1Rlc3QucXVhbGl0eSxcbiAgICApO1xuICAgIGF3YWl0IHJlc2FtcGxlci5pbml0KCk7XG4gICAgY29uc3QgZmlsZW5hbWUgPSBwYXRoLnBhcnNlKGF1ZGlvVGVzdC5pbkZpbGUpLm5hbWU7XG5cbiAgICBjb25zdCBzdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgIGNvbnN0IHJlcyA9IEJ1ZmZlci5jb25jYXQoW3Jlc2FtcGxlci5wcm9jZXNzQ2h1bmsoYXVkaW9UZXN0LnBjbURhdGEpLCByZXNhbXBsZXIucHJvY2Vzc0NodW5rKG51bGwpXSk7XG4gICAgY29uc3QgZW5kID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgY29uc29sZS5sb2coYFJlc2FtcGxlZCBpbiAke01hdGguZmxvb3IoZW5kIC0gc3RhcnQpfW1zLCBmYWN0b3IgJHsoYXVkaW9UZXN0LnBjbURhdGEubGVuZ3RoIC8gKGF1ZGlvVGVzdC5pblJhdGUgLyAxMDAwKSAvIDIgLyBhdWRpb1Rlc3QuY2hhbm5lbHMpIC8gKGVuZCAtIHN0YXJ0KX1gKTtcbiAgICBjb25zb2xlLmxvZyhgSW5wdXQgc3RyZWFtOiAke2F1ZGlvVGVzdC5wY21EYXRhLmxlbmd0aH0gYnl0ZXMsICR7YXVkaW9UZXN0LnBjbURhdGEubGVuZ3RoIC8gYXVkaW9UZXN0LmluUmF0ZSAvIDIgLyBhdWRpb1Rlc3QuY2hhbm5lbHN9c2ApO1xuICAgIGNvbnNvbGUubG9nKGBPdXRwdXQgc3RyZWFtOiAke3Jlcy5sZW5ndGh9IGJ5dGVzLCAke3Jlcy5sZW5ndGggLyBhdWRpb1Rlc3Qub3V0UmF0ZSAvIEZsb2F0MzJBcnJheS5CWVRFU19QRVJfRUxFTUVOVCAvIGF1ZGlvVGVzdC5jaGFubmVsc31zYCk7XG5cbiAgICBjb25zdCBpbnB1dER1cmF0aW9uID0gYXVkaW9UZXN0LnBjbURhdGEubGVuZ3RoIC8gYXVkaW9UZXN0LmluUmF0ZSAvIDIgLyBhdWRpb1Rlc3QuY2hhbm5lbHM7XG4gICAgY29uc3Qgb3V0cHV0RHVyYXRpb24gPSByZXMubGVuZ3RoIC8gYXVkaW9UZXN0Lm91dFJhdGUgLyBGbG9hdDMyQXJyYXkuQllURVNfUEVSX0VMRU1FTlQgLyBhdWRpb1Rlc3QuY2hhbm5lbHM7XG4gICAgYXNzZXJ0KE1hdGguYWJzKGlucHV0RHVyYXRpb24gLSBvdXRwdXREdXJhdGlvbikgPCAwLjAxLCBgU3RyZWFtIGR1cmF0aW9uIG5vdCBtYXRjaGluZyB0YXJnZXQsIGluOiAke2lucHV0RHVyYXRpb259cyAhPSBvdXQ6JHtvdXRwdXREdXJhdGlvbn1gKTtcbiAgICBjb25zb2xlLmxvZygpO1xuICAgIHdyaXRlRmlsZVN5bmMocGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgYC4uL3Jlc291cmNlcy8ke2ZpbGVuYW1lfV8ke2F1ZGlvVGVzdC5vdXRSYXRlfV8ke2F1ZGlvVGVzdC5xdWFsaXR5IHx8IDd9X291dHB1dC5wY21gKSwgcmVzKTtcbiAgfVxufVxuXG5leHBvcnQgY29uc3QgcGFyYWxsZWxUZXN0ID0gYXN5bmMgKCkgPT4ge1xuICBjb25zb2xlLmxvZygnPT09PT09PT09PT09PT09PT0nKTtcbiAgY29uc29sZS5sb2coJ1BhcmFsbGVsIFRlc3QnKTtcbiAgY29uc29sZS5sb2coJz09PT09PT09PT09PT09PT09Jyk7XG5cbiAgY29uc3Qgc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsKGF1ZGlvVGVzdHMubWFwKGFzeW5jIChhdWRpb1Rlc3QpID0+IHtcbiAgICBjb25zdCByZXNhbXBsZXIgPSBuZXcgU294clJlc2FtcGxlclRocmVhZChcbiAgICAgIGF1ZGlvVGVzdC5jaGFubmVscyxcbiAgICAgIGF1ZGlvVGVzdC5pblJhdGUsXG4gICAgICBhdWRpb1Rlc3Qub3V0UmF0ZSxcbiAgICAgIFNveHJEYXRhdHlwZS5TT1hSX0lOVDE2LFxuICAgICAgU294ckRhdGF0eXBlLlNPWFJfSU5UMTYsXG4gICAgICBhdWRpb1Rlc3QucXVhbGl0eSxcbiAgICApO1xuICAgIGF3YWl0IHJlc2FtcGxlci5pbml0KCk7XG4gICAgY29uc3QgZmlsZW5hbWUgPSBwYXRoLnBhcnNlKGF1ZGlvVGVzdC5pbkZpbGUpLm5hbWU7XG5cbiAgICBjb25zdCByZXMgPSBCdWZmZXIuY29uY2F0KFtcbiAgICAgIGF3YWl0IHJlc2FtcGxlci5wcm9jZXNzQ2h1bmsoYXVkaW9UZXN0LnBjbURhdGEpLFxuICAgICAgYXdhaXQgcmVzYW1wbGVyLnByb2Nlc3NDaHVuayhudWxsKVxuICAgIF0pO1xuICAgIGNvbnN0IGlucHV0RHVyYXRpb24gPSBhdWRpb1Rlc3QucGNtRGF0YS5sZW5ndGggLyBhdWRpb1Rlc3QuaW5SYXRlIC8gMiAvIGF1ZGlvVGVzdC5jaGFubmVscztcbiAgICBjb25zdCBvdXRwdXREdXJhdGlvbiA9IHJlcy5sZW5ndGggLyBhdWRpb1Rlc3Qub3V0UmF0ZSAvIDIgLyBhdWRpb1Rlc3QuY2hhbm5lbHM7XG4gICAgYXNzZXJ0KE1hdGguYWJzKGlucHV0RHVyYXRpb24gLSBvdXRwdXREdXJhdGlvbikgPCAwLjAxLCBgU3RyZWFtIGR1cmF0aW9uIG5vdCBtYXRjaGluZyB0YXJnZXQsIGluOiAke2lucHV0RHVyYXRpb259cyAhPSBvdXQ6JHtvdXRwdXREdXJhdGlvbn1gKTtcbiAgICByZXNhbXBsZXIuZGVzdHJveSgpO1xuICAgIC8vIHdyaXRlRmlsZVN5bmMocGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgYC4uL3Jlc291cmNlcy8ke2ZpbGVuYW1lfV8ke2F1ZGlvVGVzdC5vdXRSYXRlfV8ke2F1ZGlvVGVzdC5xdWFsaXR5IHx8IDd9X291dHB1dC5wY21gKSwgcmVzKTtcbiAgICByZXR1cm4gcmVzO1xuICB9KSk7XG4gIGNvbnN0IGVuZCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICBjb25zb2xlLmxvZyhgUmVzYW1wbGVkIGFsbCAke2F1ZGlvVGVzdHMubGVuZ3RofSBmaWxlcyBpbiAke01hdGguZmxvb3IoZW5kIC0gc3RhcnQpfW1zYCk7XG4gIC8vIGNvbnNvbGUubG9nKHJlc3VsdHMpO1xufVxuXG5jb25zdCBtYWluID0gYXN5bmMgKCkgPT4ge1xuICBhd2FpdCBwcm9taXNlQmFzZWRUZXN0KCk7XG4gIGF3YWl0IHN0cmVhbUJhc2VkVGVzdCgpO1xuICBhd2FpdCBzbWFsbENodW5rc1Rlc3QoKTtcbiAgYXdhaXQgaW5CdWZmZXJUZXN0KCk7XG4gIGF3YWl0IHR5cGVDaGFuZ2VUZXN0KCk7XG4gIGF3YWl0IHBhcmFsbGVsVGVzdCgpO1xufTtcblxubWFpbigpLmNhdGNoKChlKSA9PiB7XG4gIGNvbnNvbGUuZXJyb3IoZSk7XG4gIHByb2Nlc3MuZXhpdCgxKTtcbn0pXG4iXX0= -------------------------------------------------------------------------------- /app/test_utils.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { SoxrQuality } from './utils'; 3 | export declare const audioTests: { 4 | pcmData: Buffer; 5 | inFile: string; 6 | inRate: number; 7 | outRate: number; 8 | channels: number; 9 | quality: SoxrQuality; 10 | }[]; 11 | -------------------------------------------------------------------------------- /app/test_utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.audioTests = void 0; 7 | const path_1 = __importDefault(require("path")); 8 | const fs_1 = require("fs"); 9 | const utils_1 = require("./utils"); 10 | const audioTestsDef = [ 11 | // {inFile: path.resolve(__dirname, `../resources/24000hz_mono_test.pcm`), inRate: 24000, outRate: 48000, channels: 1}, 12 | // {inFile: path.resolve(__dirname, `../resources/24000hz_mono_test.pcm`), inRate: 24000, outRate: 48000, channels: 1, quality: SoxrQuality.SOXR_LQ}, 13 | // {inFile: path.resolve(__dirname, `../resources/24000hz_mono_test.pcm`), inRate: 24000, outRate: 48000, channels: 1, quality: SoxrQuality.SOXR_MQ}, 14 | // {inFile: path.resolve(__dirname, `../resources/24000hz_test.pcm`), inRate: 24000, outRate: 24000, channels: 2}, 15 | // {inFile: path.resolve(__dirname, `../resources/24000hz_test.pcm`), inRate: 24000, outRate: 44100, channels: 2}, 16 | { inFile: path_1.default.resolve(__dirname, `../resources/44100hz_test.pcm`), inRate: 44100, outRate: 48000, channels: 2, quality: utils_1.SoxrQuality.SOXR_LQ }, 17 | { inFile: path_1.default.resolve(__dirname, `../resources/44100hz_test.pcm`), inRate: 44100, outRate: 48000, channels: 2, quality: utils_1.SoxrQuality.SOXR_MQ }, 18 | { inFile: path_1.default.resolve(__dirname, `../resources/44100hz_test.pcm`), inRate: 44100, outRate: 48000, channels: 2, quality: utils_1.SoxrQuality.SOXR_HQ }, 19 | { inFile: path_1.default.resolve(__dirname, `../resources/44100hz_test.pcm`), inRate: 44100, outRate: 48000, channels: 2, quality: utils_1.SoxrQuality.SOXR_VHQ }, 20 | ]; 21 | exports.audioTests = audioTestsDef.map((test) => ({ 22 | ...test, 23 | pcmData: fs_1.readFileSync(test.inFile), 24 | })); 25 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdF91dGlscy5qcyIsInNvdXJjZVJvb3QiOiIvIiwic291cmNlcyI6WyJ0ZXN0X3V0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLGdEQUF3QjtBQUN4QiwyQkFBa0M7QUFDbEMsbUNBQXNDO0FBRXRDLE1BQU0sYUFBYSxHQUFHO0lBQ3BCLHVIQUF1SDtJQUN2SCxxSkFBcUo7SUFDckoscUpBQXFKO0lBQ3JKLGtIQUFrSDtJQUNsSCxrSEFBa0g7SUFDbEgsRUFBQyxNQUFNLEVBQUUsY0FBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsK0JBQStCLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLENBQUMsRUFBRSxPQUFPLEVBQUUsbUJBQVcsQ0FBQyxPQUFPLEVBQUM7SUFDNUksRUFBQyxNQUFNLEVBQUUsY0FBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsK0JBQStCLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLENBQUMsRUFBRSxPQUFPLEVBQUUsbUJBQVcsQ0FBQyxPQUFPLEVBQUM7SUFDNUksRUFBQyxNQUFNLEVBQUUsY0FBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsK0JBQStCLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLENBQUMsRUFBRSxPQUFPLEVBQUUsbUJBQVcsQ0FBQyxPQUFPLEVBQUM7SUFDNUksRUFBQyxNQUFNLEVBQUUsY0FBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsK0JBQStCLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsUUFBUSxFQUFFLENBQUMsRUFBRSxPQUFPLEVBQUUsbUJBQVcsQ0FBQyxRQUFRLEVBQUM7Q0FFOUksQ0FBQztBQUNXLFFBQUEsVUFBVSxHQUFHLGFBQWEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDckQsR0FBRyxJQUFJO0lBQ1AsT0FBTyxFQUFFLGlCQUFZLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztDQUNuQyxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xuaW1wb3J0IHsgU294clF1YWxpdHkgfSBmcm9tICcuL3V0aWxzJztcblxuY29uc3QgYXVkaW9UZXN0c0RlZiA9IFtcbiAgLy8ge2luRmlsZTogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgYC4uL3Jlc291cmNlcy8yNDAwMGh6X21vbm9fdGVzdC5wY21gKSwgaW5SYXRlOiAyNDAwMCwgb3V0UmF0ZTogNDgwMDAsIGNoYW5uZWxzOiAxfSxcbiAgLy8ge2luRmlsZTogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgYC4uL3Jlc291cmNlcy8yNDAwMGh6X21vbm9fdGVzdC5wY21gKSwgaW5SYXRlOiAyNDAwMCwgb3V0UmF0ZTogNDgwMDAsIGNoYW5uZWxzOiAxLCBxdWFsaXR5OiBTb3hyUXVhbGl0eS5TT1hSX0xRfSxcbiAgLy8ge2luRmlsZTogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgYC4uL3Jlc291cmNlcy8yNDAwMGh6X21vbm9fdGVzdC5wY21gKSwgaW5SYXRlOiAyNDAwMCwgb3V0UmF0ZTogNDgwMDAsIGNoYW5uZWxzOiAxLCBxdWFsaXR5OiBTb3hyUXVhbGl0eS5TT1hSX01RfSxcbiAgLy8ge2luRmlsZTogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgYC4uL3Jlc291cmNlcy8yNDAwMGh6X3Rlc3QucGNtYCksIGluUmF0ZTogMjQwMDAsIG91dFJhdGU6IDI0MDAwLCBjaGFubmVsczogMn0sXG4gIC8vIHtpbkZpbGU6IHBhdGgucmVzb2x2ZShfX2Rpcm5hbWUsIGAuLi9yZXNvdXJjZXMvMjQwMDBoel90ZXN0LnBjbWApLCBpblJhdGU6IDI0MDAwLCBvdXRSYXRlOiA0NDEwMCwgY2hhbm5lbHM6IDJ9LFxuICB7aW5GaWxlOiBwYXRoLnJlc29sdmUoX19kaXJuYW1lLCBgLi4vcmVzb3VyY2VzLzQ0MTAwaHpfdGVzdC5wY21gKSwgaW5SYXRlOiA0NDEwMCwgb3V0UmF0ZTogNDgwMDAsIGNoYW5uZWxzOiAyLCBxdWFsaXR5OiBTb3hyUXVhbGl0eS5TT1hSX0xRfSxcbiAge2luRmlsZTogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgYC4uL3Jlc291cmNlcy80NDEwMGh6X3Rlc3QucGNtYCksIGluUmF0ZTogNDQxMDAsIG91dFJhdGU6IDQ4MDAwLCBjaGFubmVsczogMiwgcXVhbGl0eTogU294clF1YWxpdHkuU09YUl9NUX0sXG4gIHtpbkZpbGU6IHBhdGgucmVzb2x2ZShfX2Rpcm5hbWUsIGAuLi9yZXNvdXJjZXMvNDQxMDBoel90ZXN0LnBjbWApLCBpblJhdGU6IDQ0MTAwLCBvdXRSYXRlOiA0ODAwMCwgY2hhbm5lbHM6IDIsIHF1YWxpdHk6IFNveHJRdWFsaXR5LlNPWFJfSFF9LFxuICB7aW5GaWxlOiBwYXRoLnJlc29sdmUoX19kaXJuYW1lLCBgLi4vcmVzb3VyY2VzLzQ0MTAwaHpfdGVzdC5wY21gKSwgaW5SYXRlOiA0NDEwMCwgb3V0UmF0ZTogNDgwMDAsIGNoYW5uZWxzOiAyLCBxdWFsaXR5OiBTb3hyUXVhbGl0eS5TT1hSX1ZIUX0sXG4gIC8vIHtpbkZpbGU6IHBhdGgucmVzb2x2ZShfX2Rpcm5hbWUsIGAuLi9yZXNvdXJjZXMvNDQxMDBoel90ZXN0LnBjbWApLCBpblJhdGU6IDQ0MTAwLCBvdXRSYXRlOiAyNDAwMCwgY2hhbm5lbHM6IDJ9LFxuXTtcbmV4cG9ydCBjb25zdCBhdWRpb1Rlc3RzID0gYXVkaW9UZXN0c0RlZi5tYXAoKHRlc3QpID0+ICh7XG4gIC4uLnRlc3QsXG4gIHBjbURhdGE6IHJlYWRGaWxlU3luYyh0ZXN0LmluRmlsZSksXG59KSk7XG4iXX0= -------------------------------------------------------------------------------- /app/utils.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export declare enum SoxrDatatype { 3 | SOXR_FLOAT32 = 0, 4 | SOXR_FLOAT64 = 1, 5 | SOXR_INT32 = 2, 6 | SOXR_INT16 = 3 7 | } 8 | export declare enum SoxrQuality { 9 | SOXR_QQ = 0, 10 | SOXR_LQ = 1, 11 | SOXR_MQ = 2, 12 | SOXR_HQ = 4, 13 | SOXR_VHQ = 6 14 | } 15 | export declare const bytesPerDatatypeSample: { 16 | 0: number; 17 | 1: number; 18 | 2: number; 19 | 3: number; 20 | }; 21 | export interface EmscriptenModuleSoxr extends EmscriptenModule { 22 | _soxr_create(inputRate: number, outputRate: number, num_channels: number, errPtr: number, ioSpecPtr: number, qualitySpecPtr: number, runtimeSpecPtr: number): number; 23 | _soxr_delete(resamplerPtr: number): void; 24 | _soxr_process(resamplerPtr: number, inBufPtr: number, inLen: number, inConsummedLenPtr: number, outBufPtr: number, outLen: number, outEmittedLenPtr: number): number; 25 | _soxr_io_spec(ioSpecPtr: number, itype: number, otype: number): void; 26 | _soxr_quality_spec(qualitySpecPtr: number, recipe: number, flags: number): void; 27 | _soxr_delay(ioSpecPtr: number): number; 28 | _sizeof_soxr_io_spec_t(): number; 29 | _sizeof_soxr_quality_spec_t(): number; 30 | getValue(ptr: number, type: string): any; 31 | setValue(ptr: number, value: any, type: string): any; 32 | AsciiToString(ptr: number): string; 33 | } 34 | export declare const memoize: any>(fct: T) => T; 35 | export declare const limitConcurrency: Promise>(fct: T) => T; 36 | -------------------------------------------------------------------------------- /app/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /// 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | exports.limitConcurrency = exports.memoize = exports.bytesPerDatatypeSample = exports.SoxrQuality = exports.SoxrDatatype = void 0; 5 | var SoxrDatatype; 6 | (function (SoxrDatatype) { 7 | SoxrDatatype[SoxrDatatype["SOXR_FLOAT32"] = 0] = "SOXR_FLOAT32"; 8 | SoxrDatatype[SoxrDatatype["SOXR_FLOAT64"] = 1] = "SOXR_FLOAT64"; 9 | SoxrDatatype[SoxrDatatype["SOXR_INT32"] = 2] = "SOXR_INT32"; 10 | SoxrDatatype[SoxrDatatype["SOXR_INT16"] = 3] = "SOXR_INT16"; 11 | })(SoxrDatatype = exports.SoxrDatatype || (exports.SoxrDatatype = {})); 12 | ; 13 | var SoxrQuality; 14 | (function (SoxrQuality) { 15 | SoxrQuality[SoxrQuality["SOXR_QQ"] = 0] = "SOXR_QQ"; 16 | SoxrQuality[SoxrQuality["SOXR_LQ"] = 1] = "SOXR_LQ"; 17 | SoxrQuality[SoxrQuality["SOXR_MQ"] = 2] = "SOXR_MQ"; 18 | SoxrQuality[SoxrQuality["SOXR_HQ"] = 4] = "SOXR_HQ"; 19 | SoxrQuality[SoxrQuality["SOXR_VHQ"] = 6] = "SOXR_VHQ"; 20 | })(SoxrQuality = exports.SoxrQuality || (exports.SoxrQuality = {})); 21 | exports.bytesPerDatatypeSample = { 22 | [SoxrDatatype.SOXR_FLOAT32]: 4, 23 | [SoxrDatatype.SOXR_FLOAT64]: 8, 24 | [SoxrDatatype.SOXR_INT32]: 4, 25 | [SoxrDatatype.SOXR_INT16]: 2, 26 | }; 27 | exports.memoize = (fct) => { 28 | let res = null; 29 | const resolver = (...args) => { 30 | if (res) { 31 | return res; 32 | } 33 | res = fct(...args); 34 | return res; 35 | }; 36 | return resolver; 37 | }; 38 | exports.limitConcurrency = (fct) => { 39 | let currentCall = null; 40 | const resolver = async (...args) => { 41 | if (currentCall) { 42 | await currentCall; 43 | } 44 | currentCall = fct(...args); 45 | const res = await currentCall; 46 | currentCall = null; 47 | return res; 48 | }; 49 | return resolver; 50 | }; 51 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiLyIsInNvdXJjZXMiOlsidXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLG9DQUFvQzs7O0FBRXBDLElBQVksWUFLWDtBQUxELFdBQVksWUFBWTtJQUN0QiwrREFBZ0IsQ0FBQTtJQUNoQiwrREFBZ0IsQ0FBQTtJQUNoQiwyREFBYyxDQUFBO0lBQ2QsMkRBQWMsQ0FBQTtBQUNoQixDQUFDLEVBTFcsWUFBWSxHQUFaLG9CQUFZLEtBQVosb0JBQVksUUFLdkI7QUFBQSxDQUFDO0FBRUYsSUFBWSxXQU1YO0FBTkQsV0FBWSxXQUFXO0lBQ3JCLG1EQUFXLENBQUE7SUFDWCxtREFBVyxDQUFBO0lBQ1gsbURBQVcsQ0FBQTtJQUNYLG1EQUFXLENBQUE7SUFDWCxxREFBWSxDQUFBO0FBQ2QsQ0FBQyxFQU5XLFdBQVcsR0FBWCxtQkFBVyxLQUFYLG1CQUFXLFFBTXRCO0FBRVksUUFBQSxzQkFBc0IsR0FBRztJQUNwQyxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDO0lBQzlCLENBQUMsWUFBWSxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUM7SUFDOUIsQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztJQUM1QixDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO0NBQzdCLENBQUM7QUFxQ1csUUFBQSxPQUFPLEdBQUcsQ0FBa0MsR0FBTSxFQUFNLEVBQUU7SUFDckUsSUFBSSxHQUFHLEdBQWtCLElBQUksQ0FBQztJQUM5QixNQUFNLFFBQVEsR0FBRyxDQUFDLEdBQUcsSUFBSSxFQUFFLEVBQUU7UUFDM0IsSUFBSSxHQUFHLEVBQUU7WUFDUCxPQUFPLEdBQUcsQ0FBQztTQUNaO1FBQ0QsR0FBRyxHQUFHLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQ25CLE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQyxDQUFDO0lBQ0YsT0FBTyxRQUFhLENBQUM7QUFDdkIsQ0FBQyxDQUFBO0FBRVksUUFBQSxnQkFBZ0IsR0FBRyxDQUEyQyxHQUFNLEVBQUssRUFBRTtJQUN0RixJQUFJLFdBQVcsR0FBRyxJQUFJLENBQUM7SUFDdkIsTUFBTSxRQUFRLEdBQUcsS0FBSyxFQUFFLEdBQUcsSUFBSSxFQUFFLEVBQUU7UUFDakMsSUFBSSxXQUFXLEVBQUU7WUFDZixNQUFNLFdBQVcsQ0FBQztTQUNuQjtRQUNELFdBQVcsR0FBRyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQztRQUMzQixNQUFNLEdBQUcsR0FBRyxNQUFNLFdBQVcsQ0FBQztRQUM5QixXQUFXLEdBQUcsSUFBSSxDQUFDO1FBQ25CLE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQyxDQUFDO0lBQ0YsT0FBTyxRQUFhLENBQUM7QUFDdkIsQ0FBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiLy8vIDxyZWZlcmVuY2UgdHlwZXM9XCJlbXNjcmlwdGVuXCIgLz5cblxuZXhwb3J0IGVudW0gU294ckRhdGF0eXBlIHtcbiAgU09YUl9GTE9BVDMyID0gMCxcbiAgU09YUl9GTE9BVDY0ID0gMSxcbiAgU09YUl9JTlQzMiA9IDIsXG4gIFNPWFJfSU5UMTYgPSAzLFxufTtcblxuZXhwb3J0IGVudW0gU294clF1YWxpdHkge1xuICBTT1hSX1FRID0gMCxcbiAgU09YUl9MUSA9IDEsXG4gIFNPWFJfTVEgPSAyLFxuICBTT1hSX0hRID0gNCxcbiAgU09YUl9WSFEgPSA2LFxufVxuXG5leHBvcnQgY29uc3QgYnl0ZXNQZXJEYXRhdHlwZVNhbXBsZSA9IHtcbiAgW1NveHJEYXRhdHlwZS5TT1hSX0ZMT0FUMzJdOiA0LFxuICBbU294ckRhdGF0eXBlLlNPWFJfRkxPQVQ2NF06IDgsXG4gIFtTb3hyRGF0YXR5cGUuU09YUl9JTlQzMl06IDQsXG4gIFtTb3hyRGF0YXR5cGUuU09YUl9JTlQxNl06IDIsXG59O1xuXG5leHBvcnQgaW50ZXJmYWNlIEVtc2NyaXB0ZW5Nb2R1bGVTb3hyIGV4dGVuZHMgRW1zY3JpcHRlbk1vZHVsZSB7XG4gIF9zb3hyX2NyZWF0ZShcbiAgICBpbnB1dFJhdGU6IG51bWJlcixcbiAgICBvdXRwdXRSYXRlOiBudW1iZXIsXG4gICAgbnVtX2NoYW5uZWxzOiBudW1iZXIsXG4gICAgZXJyUHRyOiBudW1iZXIsXG4gICAgaW9TcGVjUHRyOiBudW1iZXIsXG4gICAgcXVhbGl0eVNwZWNQdHI6IG51bWJlcixcbiAgICBydW50aW1lU3BlY1B0cjogbnVtYmVyLFxuICApOiBudW1iZXI7XG4gIF9zb3hyX2RlbGV0ZShyZXNhbXBsZXJQdHI6IG51bWJlcik6IHZvaWQ7XG4gIF9zb3hyX3Byb2Nlc3MoXG4gICAgcmVzYW1wbGVyUHRyOiBudW1iZXIsXG4gICAgaW5CdWZQdHI6IG51bWJlcixcbiAgICBpbkxlbjogbnVtYmVyLFxuICAgIGluQ29uc3VtbWVkTGVuUHRyOiBudW1iZXIsXG4gICAgb3V0QnVmUHRyOiBudW1iZXIsXG4gICAgb3V0TGVuOiBudW1iZXIsXG4gICAgb3V0RW1pdHRlZExlblB0cjogbnVtYmVyLFxuICApOiBudW1iZXI7XG4gIF9zb3hyX2lvX3NwZWMoXG4gICAgaW9TcGVjUHRyOiBudW1iZXIsXG4gICAgaXR5cGU6IG51bWJlcixcbiAgICBvdHlwZTogbnVtYmVyLFxuICApOiB2b2lkO1xuICBfc294cl9xdWFsaXR5X3NwZWMocXVhbGl0eVNwZWNQdHI6IG51bWJlciwgcmVjaXBlOiBudW1iZXIsIGZsYWdzOiBudW1iZXIpOiB2b2lkO1xuICBfc294cl9kZWxheShpb1NwZWNQdHI6IG51bWJlcik6IG51bWJlcjtcbiAgX3NpemVvZl9zb3hyX2lvX3NwZWNfdCgpOiBudW1iZXI7XG4gIF9zaXplb2Zfc294cl9xdWFsaXR5X3NwZWNfdCgpOiBudW1iZXI7XG5cbiAgZ2V0VmFsdWUocHRyOiBudW1iZXIsIHR5cGU6IHN0cmluZyk6IGFueTtcbiAgc2V0VmFsdWUocHRyOiBudW1iZXIsIHZhbHVlOiBhbnksIHR5cGU6IHN0cmluZyk6IGFueTtcbiAgQXNjaWlUb1N0cmluZyhwdHI6IG51bWJlcik6IHN0cmluZztcbn1cblxuZXhwb3J0IGNvbnN0IG1lbW9pemUgPSA8VCBleHRlbmRzICguLi5hcmdzOiBhbnkpID0+IGFueT4oZmN0OiBUKSA6IFQgPT4ge1xuICBsZXQgcmVzOiBSZXR1cm5UeXBlPFQ+ID0gbnVsbDtcbiAgY29uc3QgcmVzb2x2ZXIgPSAoLi4uYXJncykgPT4ge1xuICAgIGlmIChyZXMpIHtcbiAgICAgIHJldHVybiByZXM7XG4gICAgfVxuICAgIHJlcyA9IGZjdCguLi5hcmdzKTtcbiAgICByZXR1cm4gcmVzO1xuICB9O1xuICByZXR1cm4gcmVzb2x2ZXIgYXMgVDtcbn1cblxuZXhwb3J0IGNvbnN0IGxpbWl0Q29uY3VycmVuY3kgPSA8VCBleHRlbmRzICguLi5hcmdzOiBhbnkpID0+IFByb21pc2U8YW55Pj4oZmN0OiBUKTogVCA9PiB7XG4gIGxldCBjdXJyZW50Q2FsbCA9IG51bGw7XG4gIGNvbnN0IHJlc29sdmVyID0gYXN5bmMgKC4uLmFyZ3MpID0+IHtcbiAgICBpZiAoY3VycmVudENhbGwpIHtcbiAgICAgIGF3YWl0IGN1cnJlbnRDYWxsO1xuICAgIH1cbiAgICBjdXJyZW50Q2FsbCA9IGZjdCguLi5hcmdzKTtcbiAgICBjb25zdCByZXMgPSBhd2FpdCBjdXJyZW50Q2FsbDtcbiAgICBjdXJyZW50Q2FsbCA9IG51bGw7XG4gICAgcmV0dXJuIHJlcztcbiAgfTtcbiAgcmV0dXJuIHJlc29sdmVyIGFzIFQ7XG59XG4iXX0= -------------------------------------------------------------------------------- /deps/glue.c: -------------------------------------------------------------------------------- 1 | #include "soxr/src/soxr.h" 2 | 3 | int sizeof_soxr_io_spec_t() { 4 | return sizeof(soxr_io_spec_t); 5 | }; 6 | 7 | int sizeof_soxr_quality_spec_t() { 8 | return sizeof(soxr_quality_spec_t); 9 | }; 10 | -------------------------------------------------------------------------------- /deps/soxr_emscripten.patch: -------------------------------------------------------------------------------- 1 | From 59f66fdcc7e4761f837dda8819a6f3793086544a Mon Sep 17 00:00:00 2001 2 | From: Guillaume Besson 3 | Date: Mon, 20 Jul 2020 13:45:41 +0200 4 | Subject: [PATCH] Fix function table for rdft to make signature available to 5 | Emscripten 6 | 7 | --- 8 | src/cr.c | 44 ++++++++++++++++++++++++-------------------- 9 | src/cr.h | 4 ++-- 10 | src/fft4g32.c | 42 ++++++++++++++++++++++-------------------- 11 | src/pffft32.c | 32 ++++++++++++++++---------------- 12 | src/rdft_t.h | 50 +++++++++++++++++++++++++++++++++++--------------- 13 | 5 files changed, 99 insertions(+), 73 deletions(-) 14 | 15 | diff --git a/src/cr.c b/src/cr.c 16 | index 4122db3..4658c52 100644 17 | --- a/src/cr.c 18 | +++ b/src/cr.c 19 | @@ -7,6 +7,7 @@ 20 | #include 21 | #include 22 | #include 23 | +#include 24 | 25 | #include "filter.h" 26 | 27 | @@ -74,7 +75,8 @@ static void dft_stage_fn(stage_t * p, fifo_t * output_fifo) 28 | int const overlap = f->num_taps - 1; 29 | 30 | if (p->at.integer + p->L * num_in >= f->dft_length) { 31 | - fn_t const * const RDFT_CB = p->rdft_cb; 32 | + 33 | + rdft_cb_table const * const RDFT_CB = p->rdft_cb; 34 | size_t const sizeof_real = sizeof(char) << LOG2_SIZEOF_REAL(p->core_flags); 35 | div_t divd = div(f->dft_length - overlap - p->at.integer + p->L - 1, p->L); 36 | real const * input = fifo_read_ptr(&p->fifo); 37 | @@ -120,12 +122,15 @@ static void dft_stage_fn(stage_t * p, fifo_t * output_fifo) 38 | dft_out[i + 1] = 0; 39 | #undef dft_out 40 | } 41 | - if (p->step.integer > 0) 42 | + 43 | + if (p->step.integer > 0) { 44 | rdft_reorder_back(f->dft_length, f->dft_backward_setup, dft_out, p->dft_scratch); 45 | + } 46 | } else { 47 | if (p->L == 1) 48 | memcpy(dft_out, input, (size_t)f->dft_length * sizeof_real); 49 | else { 50 | + 51 | memset(dft_out, 0, (size_t)f->dft_length * sizeof_real); 52 | if (IS_FLOAT32) 53 | for (j = 0, i = p->at.integer; i < f->dft_length; ++j, i += p->L) 54 | @@ -168,7 +173,7 @@ static void dft_stage_fn(stage_t * p, fifo_t * output_fifo) 55 | memcpy(output, dft_out, (size_t)(f->dft_length >> m) * sizeof_real); 56 | fifo_trim_by(output_fifo, (((1 << m) - 1) * f->dft_length + overlap) >>m); 57 | } 58 | - (void)RDFT_CB; 59 | + (rdft_cb_table const *)RDFT_CB; 60 | } 61 | p->input_size = (f->dft_length - p->at.integer + p->L - 1) / p->L; 62 | } 63 | @@ -185,7 +190,7 @@ static void dft_stage_init( 64 | unsigned instance, double Fp, double Fs, double Fn, double att, 65 | double phase_response, stage_t * p, int L, int M, double * multiplier, 66 | unsigned min_dft_size, unsigned large_dft_size, core_flags_t core_flags, 67 | - fn_t const * RDFT_CB) 68 | + rdft_cb_table const * rdft_table) 69 | { 70 | dft_filter_t * f = &p->shared->dft_filter[instance]; 71 | int num_taps = 0, dft_length = f->dft_length, i, offset; 72 | @@ -201,9 +206,9 @@ static void dft_stage_init( 73 | else f->post_peak = num_taps / 2; 74 | 75 | dft_length = set_dft_length(num_taps, (int)min_dft_size, (int)large_dft_size); 76 | - f->coefs = rdft_calloc((size_t)dft_length, sizeof_real); 77 | + f->coefs = rdft_table->calloc((size_t)dft_length, sizeof_real); 78 | offset = dft_length - num_taps + 1; 79 | - m = (1. / dft_length) * rdft_multiplier() * L * *multiplier; 80 | + m = (1. / dft_length) * rdft_table->multiplier() * L * *multiplier; 81 | if (IS_FLOAT32) for (i = 0; i < num_taps; ++i) 82 | ((float *)f->coefs)[(i + offset) & (dft_length - 1)] =(float)(h[i] * m); 83 | else if (WITH_FLOAT64) for (i = 0; i < num_taps; ++i) 84 | @@ -211,22 +216,21 @@ static void dft_stage_init( 85 | free(h); 86 | } 87 | 88 | - if (rdft_flags() & RDFT_IS_SIMD) 89 | - p->dft_out = rdft_malloc(sizeof_real * (size_t)dft_length); 90 | - if (rdft_flags() & RDFT_NEEDS_SCRATCH) 91 | - p->dft_scratch = rdft_malloc(2 * sizeof_real * (size_t)dft_length); 92 | - 93 | + if (rdft_table->flags() & RDFT_IS_SIMD) 94 | + p->dft_out = rdft_table->malloc(sizeof_real * (size_t)dft_length); 95 | + if (rdft_table->flags() & RDFT_NEEDS_SCRATCH) 96 | + p->dft_scratch = rdft_table->malloc(2 * sizeof_real * (size_t)dft_length); 97 | if (!f->dft_length) { 98 | - void * coef_setup = rdft_forward_setup(dft_length); 99 | + void * coef_setup = rdft_table->forward_setup(dft_length); 100 | int Lp = lsx_is_power_of_2(L)? L : 1; 101 | int Mp = f_domain_m? M : 1; 102 | - f->dft_forward_setup = rdft_forward_setup(dft_length / Lp); 103 | - f->dft_backward_setup = rdft_backward_setup(dft_length / Mp); 104 | + f->dft_forward_setup = rdft_table->forward_setup(dft_length / Lp); 105 | + f->dft_backward_setup = rdft_table->backward_setup(dft_length / Mp); 106 | if (Mp == 1) 107 | - rdft_forward(dft_length, coef_setup, f->coefs, p->dft_scratch); 108 | + rdft_table->forward(dft_length, coef_setup, f->coefs, p->dft_scratch); 109 | else 110 | - rdft_oforward(dft_length, coef_setup, f->coefs, p->dft_scratch); 111 | - rdft_delete_setup(coef_setup); 112 | + rdft_table->oforward(dft_length, coef_setup, f->coefs, p->dft_scratch); 113 | + rdft_table->delete_setup(coef_setup); 114 | f->num_taps = num_taps; 115 | f->dft_length = dft_length; 116 | lsx_debug("fir_len=%i dft_length=%i Fp=%g Fs=%g Fn=%g att=%g %i/%i", 117 | @@ -235,7 +239,7 @@ static void dft_stage_init( 118 | *multiplier = 1; 119 | p->out_in_ratio = (double)L / M; 120 | p->core_flags = core_flags; 121 | - p->rdft_cb = RDFT_CB; 122 | + p->rdft_cb = rdft_table; 123 | p->fn = dft_stage_fn; 124 | p->preload = f->post_peak / L; 125 | p->at.integer = f->post_peak % L; 126 | @@ -549,7 +553,7 @@ STATIC void _soxr_flush(rate_t * p) 127 | STATIC void _soxr_close(rate_t * p) 128 | { 129 | if (p->stages) { 130 | - fn_t const * const RDFT_CB = p->core->rdft_cb; 131 | + rdft_cb_table const * const RDFT_CB = p->core->rdft_cb; 132 | rate_shared_t * shared = p->stages[0].shared; 133 | int i; 134 | 135 | @@ -570,7 +574,7 @@ STATIC void _soxr_close(rate_t * p) 136 | memset(shared, 0, sizeof(*shared)); 137 | } 138 | free(p->stages); 139 | - (void)RDFT_CB; 140 | + (rdft_cb_table const *)RDFT_CB; 141 | } 142 | } 143 | 144 | diff --git a/src/cr.h b/src/cr.h 145 | index d6e8637..b21a97c 100644 146 | --- a/src/cr.h 147 | +++ b/src/cr.h 148 | @@ -104,7 +104,7 @@ typedef struct stage { 149 | bool is_input; 150 | 151 | /* For a stage with variable (run-time generated) filter coefs: */ 152 | - fn_t const * rdft_cb; 153 | + rdft_cb_table const * rdft_cb; 154 | rate_shared_t * shared; 155 | unsigned dft_filter_num; /* Which, if any, of the 2 DFT filters to use */ 156 | real * dft_scratch; 157 | @@ -140,7 +140,7 @@ typedef struct { 158 | size_t doub_firs_len; 159 | stage_fn_t cubic_stage_fn; 160 | poly_fir_t const * poly_firs; 161 | - fn_t * rdft_cb; 162 | + rdft_cb_table * rdft_cb; 163 | } cr_core_t; 164 | 165 | typedef struct rate rate_t; 166 | diff --git a/src/fft4g32.c b/src/fft4g32.c 167 | index 7a31ba4..4e4912e 100644 168 | --- a/src/fft4g32.c 169 | +++ b/src/fft4g32.c 170 | @@ -9,28 +9,30 @@ 171 | 172 | #if WITH_CR32 173 | #include "rdft_t.h" 174 | -static void * null(void) {return 0;} 175 | -static void forward (int length, void * setup, double * H) {lsx_safe_rdft_f(length, 1, H); (void)setup;} 176 | -static void backward(int length, void * setup, double * H) {lsx_safe_rdft_f(length, -1, H); (void)setup;} 177 | +static void * null(int u1) {(void)u1; return 0;} 178 | +static void forward (int length, void * setup, void * H, void * scratch) {lsx_safe_rdft_f(length, 1, H); (void)setup; (void)scratch;} 179 | +static void backward(int length, void * setup, void * H, void * scratch) {lsx_safe_rdft_f(length, -1, H); (void)setup; (void)scratch;} 180 | static int multiplier(void) {return 2;} 181 | -static void nothing(void) {} 182 | +static void nothing(void *u1) {(void)u1;} 183 | +static void nothing2(int u1, void *u2, void *u3, void *u4) {(void)u1; (void)u2; (void)u3; (void)u4;} 184 | static int flags(void) {return 0;} 185 | 186 | -fn_t _soxr_rdft32_cb[] = { 187 | - (fn_t)null, 188 | - (fn_t)null, 189 | - (fn_t)nothing, 190 | - (fn_t)forward, 191 | - (fn_t)forward, 192 | - (fn_t)backward, 193 | - (fn_t)backward, 194 | - (fn_t)_soxr_ordered_convolve_f, 195 | - (fn_t)_soxr_ordered_partial_convolve_f, 196 | - (fn_t)multiplier, 197 | - (fn_t)nothing, 198 | - (fn_t)malloc, 199 | - (fn_t)calloc, 200 | - (fn_t)free, 201 | - (fn_t)flags, 202 | +rdft_cb_table _soxr_rdft32_cb = { 203 | + null, 204 | + null, 205 | + nothing, 206 | + forward, 207 | + forward, 208 | + backward, 209 | + backward, 210 | + _soxr_ordered_convolve_f, 211 | + _soxr_ordered_partial_convolve_f, 212 | + multiplier, 213 | + nothing2, 214 | + malloc, 215 | + calloc, 216 | + free, 217 | + flags, 218 | }; 219 | + 220 | #endif 221 | diff --git a/src/pffft32.c b/src/pffft32.c 222 | index f480809..c4c8e0a 100644 223 | --- a/src/pffft32.c 224 | +++ b/src/pffft32.c 225 | @@ -20,20 +20,20 @@ static void convolve(int length, void * setup, float * H, float const * with) { 226 | static int multiplier(void) {return 1;} 227 | static int flags(void) {return RDFT_NEEDS_SCRATCH;} 228 | 229 | -fn_t _soxr_rdft32_cb[] = { 230 | - (fn_t)setup, 231 | - (fn_t)setup, 232 | - (fn_t)delete_setup, 233 | - (fn_t)forward, 234 | - (fn_t)oforward, 235 | - (fn_t)backward, 236 | - (fn_t)obackward, 237 | - (fn_t)convolve, 238 | - (fn_t)_soxr_ordered_partial_convolve_f, 239 | - (fn_t)multiplier, 240 | - (fn_t)pffft_reorder_back, 241 | - (fn_t)malloc, 242 | - (fn_t)calloc, 243 | - (fn_t)free, 244 | - (fn_t)flags, 245 | +rdft_cb_table _soxr_rdft32_cb = { 246 | + setup, 247 | + setup, 248 | + delete_setup, 249 | + forward, 250 | + oforward, 251 | + backward, 252 | + obackward, 253 | + convolve, 254 | + _soxr_ordered_partial_convolve_f, 255 | + multiplier, 256 | + pffft_reorder_back, 257 | + malloc, 258 | + calloc, 259 | + free, 260 | + flags, 261 | }; 262 | diff --git a/src/rdft_t.h b/src/rdft_t.h 263 | index 293d9c3..26336e6 100644 264 | --- a/src/rdft_t.h 265 | +++ b/src/rdft_t.h 266 | @@ -3,21 +3,41 @@ 267 | 268 | typedef void (* fn_t)(void); 269 | 270 | -#define rdft_forward_setup (*(void * (*)(int))RDFT_CB[0]) 271 | -#define rdft_backward_setup (*(void * (*)(int))RDFT_CB[1]) 272 | -#define rdft_delete_setup (*(void (*)(void *))RDFT_CB[2]) 273 | -#define rdft_forward (*(void (*)(int, void *, void *, void *))RDFT_CB[3]) 274 | -#define rdft_oforward (*(void (*)(int, void *, void *, void *))RDFT_CB[4]) 275 | -#define rdft_backward (*(void (*)(int, void *, void *, void *))RDFT_CB[5]) 276 | -#define rdft_obackward (*(void (*)(int, void *, void *, void *))RDFT_CB[6]) 277 | -#define rdft_convolve (*(void (*)(int, void *, void *, void const *))RDFT_CB[7]) 278 | -#define rdft_convolve_portion (*(void (*)(int, void *, void const *))RDFT_CB[8]) 279 | -#define rdft_multiplier (*(int (*)(void))RDFT_CB[9]) 280 | -#define rdft_reorder_back (*(void (*)(int, void *, void *, void *))RDFT_CB[10]) 281 | -#define rdft_malloc (*(void * (*)(size_t))RDFT_CB[11]) 282 | -#define rdft_calloc (*(void * (*)(size_t, size_t))RDFT_CB[12]) 283 | -#define rdft_free (*(void (*)(void *))RDFT_CB[13]) 284 | -#define rdft_flags (*(int (*)(void))RDFT_CB[14]) 285 | +struct rdft_cb_table { 286 | + void * (* forward_setup)(int); 287 | + void * (* backward_setup)(int); 288 | + void (* delete_setup)(void *); 289 | + void (* forward)(int, void *, void *, void *); 290 | + void (* oforward)(int, void *, void *, void *); 291 | + void (* backward)(int, void *, void *, void *); 292 | + void (* obackward)(int, void *, void *, void *); 293 | + void (* convolve)(int, void *, void *, void const *); 294 | + void (* convolve_portion)(int, void *, void const *); 295 | + int (* multiplier)(void); 296 | + void (* reorder_back)(int, void *, void *, void *); 297 | + void * (* malloc)(size_t); 298 | + void * (* calloc)(size_t, size_t); 299 | + void (* free)(void *); 300 | + int (* flags)(void); 301 | +}; 302 | + 303 | +typedef struct rdft_cb_table rdft_cb_table; 304 | + 305 | +#define rdft_forward_setup RDFT_CB->forward_setup 306 | +#define rdft_backward_setup RDFT_CB->backward_setup 307 | +#define rdft_delete_setup RDFT_CB->delete_setup 308 | +#define rdft_forward RDFT_CB->forward 309 | +#define rdft_oforward RDFT_CB->oforward 310 | +#define rdft_backward RDFT_CB->backward 311 | +#define rdft_obackward RDFT_CB->obackward 312 | +#define rdft_convolve RDFT_CB->convolve 313 | +#define rdft_convolve_portion RDFT_CB->convolve_portion 314 | +#define rdft_multiplier RDFT_CB->multiplier 315 | +#define rdft_reorder_back RDFT_CB->reorder_back 316 | +#define rdft_malloc RDFT_CB->malloc 317 | +#define rdft_calloc RDFT_CB->calloc 318 | +#define rdft_free RDFT_CB->free 319 | +#define rdft_flags RDFT_CB->flags 320 | 321 | /* Flag templates: */ 322 | #define RDFT_IS_SIMD 1 323 | -- 324 | 2.27.0 325 | 326 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wasm-audio-resampler", 3 | "version": "2.0.0", 4 | "description": "An WebAssembly implementation of Soxr audio resampling lib", 5 | "main": "app/index.js", 6 | "types": "app/index.d.ts", 7 | "scripts": { 8 | "build": "tsc -b", 9 | "build:watch": "tsc -w", 10 | "deploy": "np --no-tests", 11 | "test": "node app/test.js" 12 | }, 13 | "author": "Guillaume Besson ", 14 | "repository": "https://github.com/geekuillaume/wasm-audio-resampler", 15 | "keywords": [ 16 | "audio", 17 | "resampling", 18 | "pcm", 19 | "soxr", 20 | "wasm" 21 | ], 22 | "license": "MIT", 23 | "devDependencies": { 24 | "@types/emscripten": "^1.39.4", 25 | "benny": "^3.6.14", 26 | "np": "^6.2.1", 27 | "typescript": "^3.9.6" 28 | }, 29 | "dependencies": { 30 | "threads": "^1.6.3" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/24000hz_mono_test.pcm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekuillaume/wasm-audio-resampler/93a19febc96d03ef98bede78434d3c0989373477/resources/24000hz_mono_test.pcm -------------------------------------------------------------------------------- /resources/24000hz_test.pcm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekuillaume/wasm-audio-resampler/93a19febc96d03ef98bede78434d3c0989373477/resources/24000hz_test.pcm -------------------------------------------------------------------------------- /resources/44100hz_test.pcm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekuillaume/wasm-audio-resampler/93a19febc96d03ef98bede78434d3c0989373477/resources/44100hz_test.pcm -------------------------------------------------------------------------------- /scripts/build_emscripten.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | cd deps/soxr 5 | emcmake cmake -DCMAKE_C_FLAGS="-Wno-c99-extensions" -DBUILD_SHARED_LIBS=OFF \ 6 | -DWITH_CR32S=OFF \ 7 | -DWITH_CR64=OFF \ 8 | -DWITH_CR64S=OFF \ 9 | -DWITH_VR32=OFF \ 10 | -DBUILD_TESTS=OFF \ 11 | -DWITH_LSR_BINDINGS=OFF \ 12 | -DWITH_OPENMP=OFF \ 13 | -DCMAKE_BUILD_TYPE=Release \ 14 | -DBUILD_EXAMPLES=OFF . 15 | 16 | emmake make 17 | 18 | cd ../../ 19 | 20 | emcc \ 21 | -s INITIAL_MEMORY=64MB \ 22 | -s ALLOW_MEMORY_GROWTH=1 \ 23 | -O3 \ 24 | -o src/soxr_wasm.js \ 25 | -s EXPORT_ES6=1 \ 26 | -s MODULARIZE=1 \ 27 | -s USE_ES6_IMPORT_META=0 \ 28 | -s EXPORT_NAME="Soxr" \ 29 | -s ASSERTIONS=0 \ 30 | -s NODEJS_CATCH_REJECTION=0 \ 31 | -s NODEJS_CATCH_EXIT=0 \ 32 | -s EXPORTED_RUNTIME_METHODS="['setValue', 'getValue', 'AsciiToString']" \ 33 | -s ENVIRONMENT=node,web \ 34 | -s EXPORTED_FUNCTIONS="['_malloc', '_free', '_soxr_create','_soxr_process','_soxr_delete','_soxr_io_spec','_soxr_quality_spec','_soxr_delay','_sizeof_soxr_io_spec_t','_sizeof_soxr_quality_spec_t']" \ 35 | ./deps/soxr/src/libsoxr.a ./deps/glue.c 36 | 37 | emcc \ 38 | -s INITIAL_MEMORY=64MB \ 39 | -s ALLOW_MEMORY_GROWTH=1 \ 40 | -o src/soxr_wasm_thread.js \ 41 | -O3 \ 42 | -s EXPORT_ES6=1 \ 43 | -s MODULARIZE=1 \ 44 | -s USE_ES6_IMPORT_META=0 \ 45 | -s EXPORT_NAME="Soxr" \ 46 | -s ASSERTIONS=0 \ 47 | -s NODEJS_CATCH_REJECTION=0 \ 48 | -s NODEJS_CATCH_EXIT=0 \ 49 | -s EXPORTED_RUNTIME_METHODS="['setValue', 'getValue', 'AsciiToString']" \ 50 | -s ENVIRONMENT=node,web \ 51 | -s EXPORTED_FUNCTIONS="['_malloc', '_free', '_soxr_create','_soxr_process','_soxr_delete','_soxr_io_spec','_soxr_quality_spec','_soxr_delay','_sizeof_soxr_io_spec_t','_sizeof_soxr_quality_spec_t']" \ 52 | ./deps/soxr/src/libsoxr.a ./deps/glue.c 53 | 54 | mv src/*.wasm app/ 55 | 56 | # This is necessary to set the WASM memory to shared without using emscripten pthread option which adds a lot of unused code 57 | wasm2wat app/soxr_wasm_thread.wasm -o app/soxr_wasm_thread.wat 58 | node scripts/wasm_shared_memory_transformer.js 59 | wat2wasm --enable-threads app/soxr_wasm_thread.wat -o app/soxr_wasm_thread.wasm 60 | rm app/soxr_wasm_thread.wat 61 | 62 | -------------------------------------------------------------------------------- /scripts/wasm_shared_memory_transformer.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const inputFile = fs.readFileSync(path.resolve(__dirname, '../app/soxr_wasm_thread.wat')).toString(); 5 | const output = inputFile.replace(/(\(memory \(;0;\) \d+ \d+)/g, '$1 shared'); 6 | fs.writeFileSync(path.resolve(__dirname, '../app/soxr_wasm_thread.wat'), output); 7 | -------------------------------------------------------------------------------- /src/benchmark.ts: -------------------------------------------------------------------------------- 1 | import b from 'benny'; 2 | import { SoxrResamplerThread } from './soxr_resampler_thread'; 3 | import SoxrResampler from '.'; 4 | import { audioTests } from './test_utils'; 5 | import { SoxrDatatype } from './utils'; 6 | 7 | const main = async () => { 8 | const repeatedAudioTests = [...audioTests, ...audioTests, ...audioTests].map((audioTest) => ({ 9 | ...audioTest, 10 | resampler: new SoxrResampler( 11 | audioTest.channels, 12 | audioTest.inRate, 13 | audioTest.outRate, 14 | SoxrDatatype.SOXR_INT16, 15 | SoxrDatatype.SOXR_INT16, 16 | audioTest.quality, 17 | ), 18 | threadResampler: new SoxrResamplerThread( 19 | audioTest.channels, 20 | audioTest.inRate, 21 | audioTest.outRate, 22 | SoxrDatatype.SOXR_INT16, 23 | SoxrDatatype.SOXR_INT16, 24 | audioTest.quality, 25 | ), 26 | })); 27 | 28 | for (const audioTest of repeatedAudioTests) { 29 | await audioTest.resampler.init(); 30 | await audioTest.threadResampler.init(); 31 | } 32 | 33 | await b.suite('Parallel', 34 | b.add('without worker', async () => { 35 | for (const audioTest of repeatedAudioTests) { 36 | const res = Buffer.concat([ 37 | audioTest.resampler.processChunk(audioTest.pcmData), 38 | audioTest.resampler.processChunk(null) 39 | ]); 40 | } 41 | }), 42 | b.add('with worker', async () => { 43 | await Promise.all(repeatedAudioTests.map(async (audioTest) => { 44 | const res = Buffer.concat([ 45 | await audioTest.threadResampler.processChunk(audioTest.pcmData), 46 | await audioTest.threadResampler.processChunk(null) 47 | ]); 48 | })); 49 | }), 50 | b.cycle((result, summary) => { 51 | console.log(`${result.name}: ${result.details.sampleResults.reduce((a, b) => a+b, 0) / result.details.sampleResults.length}s`) 52 | }), 53 | b.complete(), 54 | ); 55 | 56 | process.exit(0); 57 | }; 58 | 59 | main(); 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import SoxrResampler from './soxr_resampler'; 2 | import { SoxrResamplerTransform } from './soxr_transform'; 3 | import { SoxrDatatype, SoxrQuality } from './utils'; 4 | import { SoxrResamplerThread } from './soxr_resampler_thread'; 5 | 6 | export { SoxrResamplerTransform, SoxrDatatype, SoxrQuality, SoxrResamplerThread }; 7 | export default SoxrResampler; 8 | -------------------------------------------------------------------------------- /src/soxr_resampler.ts: -------------------------------------------------------------------------------- 1 | import { bytesPerDatatypeSample, EmscriptenModuleSoxr, memoize, SoxrDatatype, SoxrQuality } from './utils'; 2 | 3 | import SoxrWasm from './soxr_wasm'; 4 | 5 | class SoxrResampler { 6 | _resamplerPtr: number; 7 | _inBufferPtr = -1; 8 | _inBufferSize = -1; 9 | _outBufferPtr = -1; 10 | _outBufferSize = -1; 11 | 12 | _inProcessedLenPtr = -1; 13 | _outProcessLenPtr = -1; 14 | 15 | soxrModule: EmscriptenModuleSoxr; 16 | 17 | /** 18 | * Create an SpeexResampler tranform stream. 19 | * @param channels Number of channels, minimum is 1, no maximum 20 | * @param inRate frequency in Hz for the input chunk 21 | * @param outRate frequency in Hz for the target chunk 22 | * @param dataType type of the input and output data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 23 | * @param quality quality of the resampling, higher means more CPU usage, number between 0 and 6 24 | */ 25 | constructor( 26 | public channels, 27 | public inRate, 28 | public outRate, 29 | public inputDataType = SoxrDatatype.SOXR_FLOAT32, 30 | public outputDataType = SoxrDatatype.SOXR_FLOAT32, 31 | public quality = SoxrQuality.SOXR_HQ, 32 | ) {} 33 | 34 | init = memoize(async (moduleBuilder = SoxrWasm, opts?: any) => { 35 | this.soxrModule = await moduleBuilder(opts); 36 | }) 37 | 38 | /** 39 | * Returns the minimum size required for the outputBuffer from the provided input chunk 40 | * @param chunkOrChunkLength interleaved PCM data in this.inputDataType type or null if flush is requested 41 | */ 42 | outputBufferNeededSize(chunkOrChunkLength: Uint8Array | number) { 43 | const chunkLength = !chunkOrChunkLength ? 0 : typeof chunkOrChunkLength === 'number' ? chunkOrChunkLength : chunkOrChunkLength.length; 44 | const delaySize = this.getDelay() * bytesPerDatatypeSample[this.outputDataType] * this.channels; 45 | if (!chunkOrChunkLength) { 46 | return Math.ceil(delaySize); 47 | } 48 | return Math.ceil(delaySize + ((chunkLength / bytesPerDatatypeSample[this.inputDataType]) * this.outRate / this.inRate * bytesPerDatatypeSample[this.outputDataType])); 49 | } 50 | 51 | /** 52 | * Returns the delay introduced by the resampler in number of output samples per channel 53 | */ 54 | getDelay() { 55 | if (!this.soxrModule) { 56 | throw new Error('You need to wait for SoxrResampler.initPromise before calling this method'); 57 | } 58 | if (!this._resamplerPtr) { 59 | return 0 60 | } 61 | return this.soxrModule._soxr_delay(this._resamplerPtr); 62 | } 63 | 64 | /** 65 | * Resample a chunk of audio. 66 | * @param chunk interleaved PCM data in this.inputDataType type or null if flush is requested 67 | * @param outputBuffer Uint8Array which will store the result resampled chunk in this.outputDataType type 68 | * @returns a Uint8Array which contains the resampled data in this.outputDataType type, can be a subset of outputBuffer if it was provided 69 | */ 70 | processChunk(chunk: Uint8Array, outputBuffer?: Uint8Array) { 71 | if (!this.soxrModule) { 72 | throw new Error('You need to wait for SoxrResampler.initPromise before calling this method'); 73 | } 74 | // We check that we have as many chunks for each channel and that the last chunk is full (2 bytes) 75 | if (chunk && chunk.length % (this.channels * bytesPerDatatypeSample[this.inputDataType]) !== 0) { 76 | throw new Error(`Chunk length should be a multiple of channels * ${bytesPerDatatypeSample[this.inputDataType]} bytes`); 77 | } 78 | 79 | if (chunk) { 80 | // Resizing the input buffer in the WASM memory space to match what we need 81 | if (this._inBufferSize < chunk.length) { 82 | if (this._inBufferPtr !== -1) { 83 | this.soxrModule._free(this._inBufferPtr); 84 | } 85 | this._inBufferPtr = this.soxrModule._malloc(chunk.length); 86 | this._inBufferSize = chunk.length; 87 | } 88 | 89 | // Resizing the output buffer in the WASM memory space to match what we need 90 | const outBufferLengthTarget = this.outputBufferNeededSize(chunk); 91 | if (this._outBufferSize < outBufferLengthTarget) { 92 | if (this._outBufferPtr !== -1) { 93 | this.soxrModule._free(this._outBufferPtr); 94 | } 95 | this._outBufferPtr = this.soxrModule._malloc(outBufferLengthTarget); 96 | this._outBufferSize = outBufferLengthTarget; 97 | } 98 | 99 | // Copying the info from the input Buffer in the WASM memory space 100 | this.soxrModule.HEAPU8.set(chunk, this._inBufferPtr); 101 | } 102 | 103 | const outSamplesPerChannelsWritten = this.processInternalBuffer(chunk ? chunk.length : 0); 104 | 105 | const outputLength = outSamplesPerChannelsWritten * this.channels * bytesPerDatatypeSample[this.outputDataType]; 106 | if (!outputBuffer) { 107 | outputBuffer = new Uint8Array(outputLength); 108 | } 109 | if (outputBuffer.length < outputLength) { 110 | throw new Error(`Provided outputBuffer is too small: ${outputBuffer.length} < ${outputLength}`); 111 | } 112 | outputBuffer.set(this.soxrModule.HEAPU8.subarray( 113 | this._outBufferPtr, 114 | this._outBufferPtr + outSamplesPerChannelsWritten * this.channels * bytesPerDatatypeSample[this.outputDataType] 115 | )); 116 | if (outputBuffer.length !== outputLength) { 117 | return new Uint8Array(outputBuffer.buffer, outputBuffer.byteOffset, outputLength); 118 | } else { 119 | return outputBuffer; 120 | } 121 | } 122 | 123 | prepareInternalBuffers(chunkLength: number) { 124 | // Resizing the input buffer in the WASM memory space to match what we need 125 | if (this._inBufferSize < chunkLength) { 126 | if (this._inBufferPtr !== -1) { 127 | this.soxrModule._free(this._inBufferPtr); 128 | } 129 | this._inBufferPtr = this.soxrModule._malloc(chunkLength); 130 | this._inBufferSize = chunkLength; 131 | } 132 | 133 | // Resizing the output buffer in the WASM memory space to match what we need 134 | const outBufferLengthTarget = this.outputBufferNeededSize(chunkLength); 135 | if (this._outBufferSize < outBufferLengthTarget) { 136 | if (this._outBufferPtr !== -1) { 137 | this.soxrModule._free(this._outBufferPtr); 138 | } 139 | this._outBufferPtr = this.soxrModule._malloc(outBufferLengthTarget); 140 | this._outBufferSize = outBufferLengthTarget; 141 | } 142 | } 143 | 144 | processInternalBuffer(chunkLength: number) { 145 | if (!this.soxrModule) { 146 | throw new Error('You need to wait for SoxrResampler.initPromise before calling this method'); 147 | } 148 | 149 | if (!this._resamplerPtr) { 150 | const ioSpecPtr = this.soxrModule._malloc(this.soxrModule._sizeof_soxr_io_spec_t()); 151 | this.soxrModule._soxr_io_spec(ioSpecPtr, this.inputDataType, this.outputDataType); 152 | const qualitySpecPtr = this.soxrModule._malloc(this.soxrModule._sizeof_soxr_quality_spec_t()); 153 | this.soxrModule._soxr_quality_spec(qualitySpecPtr, this.quality, 0); 154 | const errPtr = this.soxrModule._malloc(4); 155 | this._resamplerPtr = this.soxrModule._soxr_create( 156 | this.inRate, 157 | this.outRate, 158 | this.channels, 159 | errPtr, 160 | ioSpecPtr, 161 | qualitySpecPtr, 162 | 0, 163 | ); 164 | this.soxrModule._free(ioSpecPtr); 165 | this.soxrModule._free(qualitySpecPtr); 166 | const errNum = this.soxrModule.getValue(errPtr, 'i32'); 167 | if (errNum !== 0) { 168 | const err = new Error(this.soxrModule.AsciiToString(errNum)); 169 | this.soxrModule._free(errPtr); 170 | throw err; 171 | } 172 | this._inProcessedLenPtr = this.soxrModule._malloc(Uint32Array.BYTES_PER_ELEMENT); 173 | this._outProcessLenPtr = this.soxrModule._malloc(Uint32Array.BYTES_PER_ELEMENT); 174 | } 175 | 176 | // number of samples per channel in input buffer 177 | this.soxrModule.setValue(this._inProcessedLenPtr, 0, 'i32'); 178 | 179 | // number of samples per channels available in output buffer 180 | this.soxrModule.setValue(this._outProcessLenPtr, 0, 'i32'); 181 | 182 | const errPtr = this.soxrModule._soxr_process( 183 | this._resamplerPtr, 184 | chunkLength ? this._inBufferPtr : 0, 185 | chunkLength ? chunkLength / this.channels / bytesPerDatatypeSample[this.inputDataType] : 0, 186 | this._inProcessedLenPtr, 187 | this._outBufferPtr, 188 | this._outBufferSize / this.channels / bytesPerDatatypeSample[this.outputDataType], 189 | this._outProcessLenPtr, 190 | ); 191 | 192 | if (errPtr !== 0) { 193 | throw new Error(this.soxrModule.AsciiToString(errPtr)); 194 | } 195 | const outSamplesPerChannelsWritten = this.soxrModule.getValue(this._outProcessLenPtr, 'i32'); 196 | return outSamplesPerChannelsWritten; 197 | } 198 | } 199 | 200 | export default SoxrResampler; 201 | -------------------------------------------------------------------------------- /src/soxr_resampler_thread.ts: -------------------------------------------------------------------------------- 1 | import { spawn, Thread, Worker } from "threads" 2 | 3 | import { SoxrDatatype, bytesPerDatatypeSample, SoxrQuality, memoize, limitConcurrency } from './utils'; 4 | import {exposed} from './soxr_resampler_worker'; 5 | 6 | export class SoxrResamplerThread { 7 | private worker; 8 | 9 | /** 10 | * Create an SpeexResampler tranform stream. 11 | * @param channels Number of channels, minimum is 1, no maximum 12 | * @param inRate frequency in Hz for the input chunk 13 | * @param outRate frequency in Hz for the target chunk 14 | * @param dataType type of the input and output data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 15 | * @param quality quality of the resampling, higher means more CPU usage, number between 0 and 6 16 | */ 17 | constructor( 18 | public channels: number, 19 | public inRate: number, 20 | public outRate: number, 21 | public inputDataType = SoxrDatatype.SOXR_FLOAT32, 22 | public outputDataType = SoxrDatatype.SOXR_FLOAT32, 23 | public quality = SoxrQuality.SOXR_HQ, 24 | ) {} 25 | 26 | init = memoize(async () =>{ 27 | this.worker = await spawn(new Worker('./soxr_resampler_worker.js')); 28 | await this.worker.init(this.channels, this.inRate, this.outRate, this.inputDataType, this.outputDataType, this.quality); 29 | }) 30 | 31 | /** 32 | * Returns the minimum size required for the outputBuffer from the provided input chunk 33 | * @param chunk interleaved PCM data in this.inputDataType type or null if flush is requested 34 | */ 35 | async outputBufferNeededSize(chunk: Uint8Array) { 36 | return await this.worker.outputBufferNeededSize(chunk ? chunk.length : 0); 37 | } 38 | 39 | /** 40 | * Returns the delay introduced by the resampler in number of output samples per channel 41 | */ 42 | async getDelay() { 43 | return await this.worker.getDelay(); 44 | } 45 | 46 | /** 47 | * Resample a chunk of audio. 48 | * @param chunk interleaved PCM data in this.inputDataType type or null if flush is requested 49 | * @param outputBuffer Uint8Array which will store the result resampled chunk in this.outputDataType type 50 | */ 51 | processChunk = limitConcurrency(async (chunk: Uint8Array, outputBuffer?: Uint8Array) => { 52 | const chunkLength = chunk ? chunk.length : 0; 53 | const { inBufferPtr, outBufferPtr, memory } = await this.worker.prepareInBuffer(chunkLength); 54 | 55 | const HEAPU8 = new Int8Array(memory); 56 | if (chunk) { 57 | HEAPU8.set(chunk, inBufferPtr); 58 | } 59 | 60 | const outSamplesPerChannelsWritten = await this.worker.processInternalBuffer(chunkLength); 61 | const outputLength = outSamplesPerChannelsWritten * this.channels * bytesPerDatatypeSample[this.outputDataType]; 62 | if (!outputBuffer) { 63 | outputBuffer = new Uint8Array(outputLength); 64 | } 65 | if (outputBuffer.length < outputLength) { 66 | throw new Error(`Provided outputBuffer is too small: ${outputBuffer.length} < ${outputLength}`); 67 | } 68 | outputBuffer.set(HEAPU8.subarray( 69 | outBufferPtr, 70 | outBufferPtr + outSamplesPerChannelsWritten * this.channels * bytesPerDatatypeSample[this.outputDataType] 71 | )); 72 | if (outputBuffer.length !== outputLength) { 73 | return outputBuffer.subarray(0, outputLength); 74 | } else { 75 | return outputBuffer; 76 | } 77 | }) 78 | 79 | destroy() { 80 | Thread.terminate(this.worker); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/soxr_resampler_worker.ts: -------------------------------------------------------------------------------- 1 | import SoxrResampler from './soxr_resampler' 2 | import { expose } from "threads/worker" 3 | import { SoxrDatatype, SoxrQuality } from './utils'; 4 | import SoxrWasmThreaded from './soxr_wasm_thread'; 5 | 6 | const INITIAL_MEMORY = 64 * 1024 * 1024; 7 | const WASM_PAGE_SIZE = 65536; 8 | 9 | let resampler: SoxrResampler; 10 | 11 | export const exposed = { 12 | async init( 13 | channels: number, 14 | inRate: number, 15 | outRate: number, 16 | inputDataType = SoxrDatatype.SOXR_FLOAT32, 17 | outputDataType = SoxrDatatype.SOXR_FLOAT32, 18 | quality = SoxrQuality.SOXR_HQ, 19 | ) { 20 | resampler = new SoxrResampler( 21 | channels, 22 | inRate, 23 | outRate, 24 | inputDataType, 25 | outputDataType, 26 | quality, 27 | ); 28 | const wasmMemory = new WebAssembly.Memory({ 29 | initial: INITIAL_MEMORY / WASM_PAGE_SIZE, 30 | maximum: 2147483648 / WASM_PAGE_SIZE, 31 | // @ts-ignore 32 | shared: true, 33 | }); 34 | if (!(wasmMemory.buffer instanceof SharedArrayBuffer)) { 35 | throw new Error('Runtime doesn\'t have thread support, if running on NodeJS, use --experimental-wasm-threads flag') 36 | } 37 | await resampler.init(SoxrWasmThreaded, { 38 | wasmMemory, 39 | }); 40 | }, 41 | 42 | prepareInBuffer(chunkLength: number) { 43 | resampler.prepareInternalBuffers(chunkLength); 44 | 45 | return { 46 | inBufferPtr: resampler._inBufferPtr, 47 | outBufferPtr: resampler._outBufferPtr, 48 | // @ts-ignore 49 | memory: resampler.soxrModule.wasmMemory.buffer, 50 | }; 51 | }, 52 | 53 | processInternalBuffer(chunkLength: number) { 54 | return resampler.processInternalBuffer(chunkLength); 55 | }, 56 | 57 | outputBufferNeededSize(chunkOrChunkLength: Uint8Array | number) { 58 | return resampler.outputBufferNeededSize(chunkOrChunkLength); 59 | }, 60 | 61 | getDelay() { 62 | return resampler.getDelay(); 63 | }, 64 | }; 65 | 66 | expose(exposed); 67 | -------------------------------------------------------------------------------- /src/soxr_transform.ts: -------------------------------------------------------------------------------- 1 | import SoxrResampler from './soxr_resampler'; 2 | import { Transform } from 'stream'; 3 | import { bytesPerDatatypeSample, SoxrDatatype, SoxrQuality } from './utils'; 4 | 5 | const EMPTY_BUFFER = Buffer.alloc(0); 6 | 7 | export class SoxrResamplerTransform extends Transform { 8 | resampler: SoxrResampler; 9 | _alignementBuffer: Buffer; 10 | 11 | private initPromise; 12 | 13 | /** 14 | * Create an SpeexResampler instance. 15 | * @param channels Number of channels, minimum is 1, no maximum 16 | * @param inRate frequency in Hz for the input chunk 17 | * @param outRate frequency in Hz for the target chunk 18 | * @param inputDataType type of the input data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 19 | * @param outputDataType type of the output data, 0 = Float32, 1 = Float64, 2 = Int32, 3 = Int16 20 | * @param quality quality of the resampling, higher means more CPU usage, number between 0 and 6 21 | */ 22 | constructor( 23 | public channels, 24 | public inRate, 25 | public outRate, 26 | public inputDataType = SoxrDatatype.SOXR_FLOAT32, 27 | public outputDataType = SoxrDatatype.SOXR_FLOAT32, 28 | public quality = SoxrQuality.SOXR_HQ, 29 | ) { 30 | super(); 31 | this.resampler = new SoxrResampler(channels, inRate, outRate, inputDataType, outputDataType, quality); 32 | this.initPromise = this.resampler.init(); 33 | this.initPromise.then(() => { 34 | this.initPromise = null; 35 | }); 36 | this.channels = channels; 37 | this._alignementBuffer = EMPTY_BUFFER; 38 | } 39 | 40 | _transform(chunk, encoding, callback) { 41 | let chunkToProcess: Buffer = chunk; 42 | if (this._alignementBuffer.length > 0) { 43 | chunkToProcess = Buffer.concat([ 44 | this._alignementBuffer, 45 | chunk, 46 | ]); 47 | this._alignementBuffer = EMPTY_BUFFER; 48 | } 49 | // the resampler needs a buffer aligned to 16bits times the number of channels 50 | // so we keep the extraneous bytes in a buffer for next chunk 51 | const extraneousBytesCount = chunkToProcess.length % (this.channels * bytesPerDatatypeSample[this.inputDataType]); 52 | if (extraneousBytesCount !== 0) { 53 | this._alignementBuffer = Buffer.from(chunkToProcess.slice(chunkToProcess.length - extraneousBytesCount)); 54 | chunkToProcess = chunkToProcess.slice(0, chunkToProcess.length - extraneousBytesCount); 55 | } 56 | try { 57 | if (this.initPromise) { 58 | this.initPromise.then(() => { 59 | const res = this.resampler.processChunk(chunkToProcess); 60 | callback(null, res); 61 | }).catch((e) => { 62 | callback(e); 63 | }); 64 | } else { 65 | const res = this.resampler.processChunk(chunkToProcess); 66 | callback(null, res); 67 | } 68 | } catch (e) { 69 | callback(e); 70 | } 71 | } 72 | 73 | _flush(callback) { 74 | try { 75 | const res = this.resampler.processChunk(null); 76 | callback(null, res); 77 | } catch (e) { 78 | callback(e); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/soxr_wasm.js: -------------------------------------------------------------------------------- 1 | 2 | var Soxr = (function() { 3 | var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; 4 | if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; 5 | return ( 6 | function(Soxr) { 7 | Soxr = Soxr || {}; 8 | 9 | var Module=typeof Soxr!=="undefined"?Soxr:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}function setValue(ptr,value,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":HEAP8[ptr>>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}function getValue(ptr,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":return HEAP8[ptr>>0];case"i8":return HEAP8[ptr>>0];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP32[ptr>>2];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];default:abort("invalid type for getValue: "+type)}return null}var wasmMemory;var wasmTable=new WebAssembly.Table({"initial":44,"maximum":44,"element":"anyfunc"});var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function AsciiToString(ptr){var str="";while(1){var ch=HEAPU8[ptr++>>0];if(!ch)return str;str+=String.fromCharCode(ch)}}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_INITIAL_MEMORY=Module["INITIAL_MEMORY"]||67108864;if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_INITIAL_MEMORY/WASM_PAGE_SIZE,"maximum":2147483648/WASM_PAGE_SIZE})}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_INITIAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var Math_abs=Math.abs;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_min=Math.min;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var wasmBinaryFile="soxr_wasm.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}var tempDouble;var tempI64;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator==="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j>2]=num;return 0}function _time(ptr){var ret=Date.now()/1e3|0;if(ptr){HEAP32[ptr>>2]=ret}return ret}var asmLibraryArg={"b":wasmTable,"i":_emscripten_memcpy_big,"j":_emscripten_resize_heap,"f":_environ_get,"g":_environ_sizes_get,"h":_fd_close,"d":_fd_seek,"c":_fd_write,"a":wasmMemory,"e":_time};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["k"]).apply(null,arguments)};var _soxr_quality_spec=Module["_soxr_quality_spec"]=function(){return(_soxr_quality_spec=Module["_soxr_quality_spec"]=Module["asm"]["l"]).apply(null,arguments)};var _soxr_io_spec=Module["_soxr_io_spec"]=function(){return(_soxr_io_spec=Module["_soxr_io_spec"]=Module["asm"]["m"]).apply(null,arguments)};var _soxr_create=Module["_soxr_create"]=function(){return(_soxr_create=Module["_soxr_create"]=Module["asm"]["n"]).apply(null,arguments)};var _soxr_delete=Module["_soxr_delete"]=function(){return(_soxr_delete=Module["_soxr_delete"]=Module["asm"]["o"]).apply(null,arguments)};var _soxr_delay=Module["_soxr_delay"]=function(){return(_soxr_delay=Module["_soxr_delay"]=Module["asm"]["p"]).apply(null,arguments)};var _soxr_process=Module["_soxr_process"]=function(){return(_soxr_process=Module["_soxr_process"]=Module["asm"]["q"]).apply(null,arguments)};var _sizeof_soxr_io_spec_t=Module["_sizeof_soxr_io_spec_t"]=function(){return(_sizeof_soxr_io_spec_t=Module["_sizeof_soxr_io_spec_t"]=Module["asm"]["r"]).apply(null,arguments)};var _sizeof_soxr_quality_spec_t=Module["_sizeof_soxr_quality_spec_t"]=function(){return(_sizeof_soxr_quality_spec_t=Module["_sizeof_soxr_quality_spec_t"]=Module["asm"]["s"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["t"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["u"]).apply(null,arguments)};Module["setValue"]=setValue;Module["getValue"]=getValue;Module["AsciiToString"]=AsciiToString;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run(); 10 | 11 | 12 | return Soxr.ready 13 | } 14 | ); 15 | })(); 16 | export default Soxr; -------------------------------------------------------------------------------- /src/soxr_wasm_thread.js: -------------------------------------------------------------------------------- 1 | 2 | var Soxr = (function() { 3 | var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; 4 | if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; 5 | return ( 6 | function(Soxr) { 7 | Soxr = Soxr || {}; 8 | 9 | var Module=typeof Soxr!=="undefined"?Soxr:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function shell_read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function readBinary(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function readAsync(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function xhr_onload(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}function setValue(ptr,value,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":HEAP8[ptr>>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}function getValue(ptr,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":return HEAP8[ptr>>0];case"i8":return HEAP8[ptr>>0];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP32[ptr>>2];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];default:abort("invalid type for getValue: "+type)}return null}var wasmMemory;var wasmTable=new WebAssembly.Table({"initial":44,"maximum":44,"element":"anyfunc"});var ABORT=false;var EXITSTATUS=0;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function AsciiToString(ptr){var str="";while(1){var ch=HEAPU8[ptr++>>0];if(!ch)return str;str+=String.fromCharCode(ch)}}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}var WASM_PAGE_SIZE=65536;function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_INITIAL_MEMORY=Module["INITIAL_MEMORY"]||67108864;if(Module["wasmMemory"]){wasmMemory=Module["wasmMemory"]}else{wasmMemory=new WebAssembly.Memory({"initial":INITIAL_INITIAL_MEMORY/WASM_PAGE_SIZE,"maximum":2147483648/WASM_PAGE_SIZE})}if(wasmMemory){buffer=wasmMemory.buffer}INITIAL_INITIAL_MEMORY=buffer.byteLength;updateGlobalBufferAndViews(buffer);var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var Math_abs=Math.abs;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_min=Math.min;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var wasmBinaryFile="soxr_wasm_thread.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(){try{if(wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(wasmBinaryFile)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary()})}return Promise.resolve().then(getBinary)}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&typeof fetch==="function"){fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}var tempDouble;var tempI64;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator==="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};var SYSCALLS={mappings:{},buffers:[null,[],[]],printChar:function(stream,curr){var buffer=SYSCALLS.buffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j>2]=num;return 0}function _time(ptr){var ret=Date.now()/1e3|0;if(ptr){HEAP32[ptr>>2]=ret}return ret}var asmLibraryArg={"b":wasmTable,"i":_emscripten_memcpy_big,"j":_emscripten_resize_heap,"f":_environ_get,"g":_environ_sizes_get,"h":_fd_close,"d":_fd_seek,"c":_fd_write,"a":wasmMemory,"e":_time};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["k"]).apply(null,arguments)};var _soxr_quality_spec=Module["_soxr_quality_spec"]=function(){return(_soxr_quality_spec=Module["_soxr_quality_spec"]=Module["asm"]["l"]).apply(null,arguments)};var _soxr_io_spec=Module["_soxr_io_spec"]=function(){return(_soxr_io_spec=Module["_soxr_io_spec"]=Module["asm"]["m"]).apply(null,arguments)};var _soxr_create=Module["_soxr_create"]=function(){return(_soxr_create=Module["_soxr_create"]=Module["asm"]["n"]).apply(null,arguments)};var _soxr_delete=Module["_soxr_delete"]=function(){return(_soxr_delete=Module["_soxr_delete"]=Module["asm"]["o"]).apply(null,arguments)};var _soxr_delay=Module["_soxr_delay"]=function(){return(_soxr_delay=Module["_soxr_delay"]=Module["asm"]["p"]).apply(null,arguments)};var _soxr_process=Module["_soxr_process"]=function(){return(_soxr_process=Module["_soxr_process"]=Module["asm"]["q"]).apply(null,arguments)};var _sizeof_soxr_io_spec_t=Module["_sizeof_soxr_io_spec_t"]=function(){return(_sizeof_soxr_io_spec_t=Module["_sizeof_soxr_io_spec_t"]=Module["asm"]["r"]).apply(null,arguments)};var _sizeof_soxr_quality_spec_t=Module["_sizeof_soxr_quality_spec_t"]=function(){return(_sizeof_soxr_quality_spec_t=Module["_sizeof_soxr_quality_spec_t"]=Module["asm"]["s"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["t"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["u"]).apply(null,arguments)};Module["setValue"]=setValue;Module["getValue"]=getValue;Module["AsciiToString"]=AsciiToString;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run(); 10 | 11 | 12 | return Soxr.ready 13 | } 14 | ); 15 | })(); 16 | export default Soxr; -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | import {writeFileSync,createReadStream} from 'fs'; 2 | // const {promisify} = require('util'); 3 | import { performance } from 'perf_hooks' 4 | import path from 'path'; 5 | import { audioTests } from './test_utils'; 6 | 7 | import SoxrResampler, {SoxrResamplerTransform, SoxrDatatype, SoxrQuality} from './index'; 8 | import { SoxrResamplerThread } from './soxr_resampler_thread'; 9 | 10 | const assert = (condition, message) => { 11 | if (!condition) { 12 | throw new Error(message); 13 | } 14 | } 15 | 16 | export const promiseBasedTest = async () => { 17 | for (const audioTest of audioTests) { 18 | const resampler = new SoxrResampler( 19 | audioTest.channels, 20 | audioTest.inRate, 21 | audioTest.outRate, 22 | SoxrDatatype.SOXR_INT16, 23 | SoxrDatatype.SOXR_INT16, 24 | audioTest.quality, 25 | ); 26 | await resampler.init(); 27 | const filename = path.parse(audioTest.inFile).name; 28 | 29 | const start = performance.now(); 30 | const res = Buffer.concat([resampler.processChunk(audioTest.pcmData), resampler.processChunk(null)]); 31 | const end = performance.now(); 32 | // console.log(res); 33 | console.log(`Resampling file ${audioTest.inFile} with ${audioTest.channels} channel(s) from ${audioTest.inRate}Hz to ${audioTest.outRate}Hz with quality ${audioTest.quality || 4}`); 34 | console.log(`Resampled in ${Math.floor(end - start)}ms, factor ${(audioTest.pcmData.length / (audioTest.inRate / 1000) / 2 / audioTest.channels) / (end - start)}`); 35 | console.log(`Input stream: ${audioTest.pcmData.length} bytes, ${audioTest.pcmData.length / audioTest.inRate / 2 / audioTest.channels}s`); 36 | console.log(`Output stream: ${res.length} bytes, ${res.length / audioTest.outRate / 2 / audioTest.channels}s`); 37 | console.log(); 38 | 39 | const inputDuration = audioTest.pcmData.length / audioTest.inRate / 2 / audioTest.channels; 40 | const outputDuration = res.length / audioTest.outRate / 2 / audioTest.channels; 41 | assert(Math.abs(inputDuration - outputDuration) < 0.01, `Stream duration not matching target, in: ${inputDuration}s != out:${outputDuration}`); 42 | // writeFileSync(path.resolve(__dirname, `../resources/${filename}_${audioTest.outRate}_${audioTest.quality || 7}_output.pcm`), res); 43 | } 44 | } 45 | 46 | const streamBasedTest = async () => { 47 | console.log('================='); 48 | console.log('Tranform Stream Test'); 49 | console.log('================='); 50 | 51 | for (const audioTest of audioTests) { 52 | console.log(`Resampling file ${audioTest.inFile} with ${audioTest.channels} channel(s) from ${audioTest.inRate}Hz to ${audioTest.outRate}Hz with quality ${audioTest.quality || 4}`); 53 | const readFileStream = createReadStream(audioTest.inFile); 54 | const transformStream = new SoxrResamplerTransform( 55 | audioTest.channels, 56 | audioTest.inRate, 57 | audioTest.outRate, 58 | SoxrDatatype.SOXR_INT16, 59 | SoxrDatatype.SOXR_INT16, 60 | audioTest.quality, 61 | ); 62 | let pcmData = Buffer.alloc(0); 63 | readFileStream.on('data', (d) => { 64 | pcmData = Buffer.concat([ pcmData, d as Buffer ]); 65 | }); 66 | let res = Buffer.alloc(0); 67 | transformStream.on('data', (d) => { 68 | res = Buffer.concat([ res, d as Buffer ]); 69 | }); 70 | 71 | const start = performance.now(); 72 | readFileStream.pipe(transformStream); 73 | await new Promise((r) => transformStream.on('end', r)); 74 | const end = performance.now(); 75 | console.log(`Resampled in ${Math.floor(end - start)}ms, factor ${(pcmData.length / (audioTest.inRate / 1000) / 2 / audioTest.channels) / (end - start)}`); 76 | console.log(`Input stream: ${pcmData.length} bytes, ${pcmData.length / audioTest.inRate / 2 / audioTest.channels}s`); 77 | console.log(`Output stream: ${res.length} bytes, ${res.length / audioTest.outRate / 2 / audioTest.channels}s`); 78 | 79 | const inputDuration = pcmData.length / audioTest.inRate / 2 / audioTest.channels; 80 | const outputDuration = res.length / audioTest.outRate / 2 / audioTest.channels; 81 | assert(Math.abs(inputDuration - outputDuration) < 0.01, `Stream duration not matching target, in: ${inputDuration}s != out:${outputDuration}`); 82 | console.log(); 83 | } 84 | } 85 | 86 | const smallChunksTest = async () => { 87 | console.log('================='); 88 | console.log('Small chunks Test'); 89 | console.log('================='); 90 | 91 | for (const audioTest of audioTests) { 92 | const chunkSize = (audioTest.inRate / 100) * 2 * audioTest.channels; // simulate 100 chunks per seconds 93 | console.log(`Resampling file ${audioTest.inFile} with ${audioTest.channels} channel(s) from ${audioTest.inRate}Hz to ${audioTest.outRate}Hz with quality ${audioTest.quality || 4}`); 94 | const resampler = new SoxrResampler( 95 | audioTest.channels, 96 | audioTest.inRate, 97 | audioTest.outRate, 98 | SoxrDatatype.SOXR_INT16, 99 | SoxrDatatype.SOXR_INT16, 100 | audioTest.quality, 101 | ); 102 | await resampler.init(); 103 | 104 | const start = performance.now(); 105 | for (let i = 0; i * chunkSize < audioTest.pcmData.length; i++) { 106 | const chunk = audioTest.pcmData.slice(i * chunkSize, (i + 1) * chunkSize); 107 | const res = resampler.processChunk(chunk); 108 | // if (res.length !== (audioTest.outRate / 100) * 2 * audioTest.channels) { 109 | // console.log('Diff length:', res.length); 110 | // } 111 | } 112 | const end = performance.now(); 113 | 114 | console.log(`Resampled in ${Math.floor(end - start)}ms, factor ${(audioTest.pcmData.length / (audioTest.inRate / 1000) / 2 / audioTest.channels) / (end - start)}`); 115 | 116 | console.log(); 117 | } 118 | } 119 | 120 | const inBufferTest = async () => { 121 | console.log('================='); 122 | console.log('In buffer small chunks test'); 123 | console.log('================='); 124 | 125 | const outputBuffer = new Uint8Array(2 * 1024 * 1024); // 2MB, should be enough for this test 126 | 127 | for (const audioTest of audioTests) { 128 | const chunkSize = (audioTest.inRate / 100) * 2 * audioTest.channels; // simulate 100 chunks per seconds 129 | console.log(`Resampling file ${audioTest.inFile} with ${audioTest.channels} channel(s) from ${audioTest.inRate}Hz to ${audioTest.outRate}Hz with quality ${audioTest.quality || 4}`); 130 | const resampler = new SoxrResampler( 131 | audioTest.channels, 132 | audioTest.inRate, 133 | audioTest.outRate, 134 | SoxrDatatype.SOXR_INT16, 135 | SoxrDatatype.SOXR_INT16, 136 | audioTest.quality, 137 | ); 138 | await resampler.init(); 139 | 140 | const start = performance.now(); 141 | for (let i = 0; i * chunkSize < audioTest.pcmData.length; i++) { 142 | const chunk = audioTest.pcmData.slice(i * chunkSize, (i + 1) * chunkSize); 143 | resampler.processChunk(chunk, outputBuffer); 144 | } 145 | const end = performance.now(); 146 | 147 | console.log(`Resampled in ${Math.floor(end - start)}ms, factor ${(audioTest.pcmData.length / (audioTest.inRate / 1000) / 2 / audioTest.channels) / (end - start)}`); 148 | 149 | console.log(); 150 | } 151 | } 152 | 153 | const typeChangeTest = async () => { 154 | console.log('================='); 155 | console.log('Type change test'); 156 | console.log('================='); 157 | 158 | for (const audioTest of audioTests) { 159 | console.log(`Resampling file ${audioTest.inFile} with ${audioTest.channels} channel(s) from ${audioTest.inRate}Hz to ${audioTest.outRate}Hz with quality ${audioTest.quality || 4}`); 160 | const resampler = new SoxrResampler( 161 | audioTest.channels, 162 | audioTest.inRate, 163 | audioTest.outRate, 164 | SoxrDatatype.SOXR_INT16, 165 | SoxrDatatype.SOXR_FLOAT32, 166 | audioTest.quality, 167 | ); 168 | await resampler.init(); 169 | const filename = path.parse(audioTest.inFile).name; 170 | 171 | const start = performance.now(); 172 | const res = Buffer.concat([resampler.processChunk(audioTest.pcmData), resampler.processChunk(null)]); 173 | const end = performance.now(); 174 | console.log(`Resampled in ${Math.floor(end - start)}ms, factor ${(audioTest.pcmData.length / (audioTest.inRate / 1000) / 2 / audioTest.channels) / (end - start)}`); 175 | console.log(`Input stream: ${audioTest.pcmData.length} bytes, ${audioTest.pcmData.length / audioTest.inRate / 2 / audioTest.channels}s`); 176 | console.log(`Output stream: ${res.length} bytes, ${res.length / audioTest.outRate / Float32Array.BYTES_PER_ELEMENT / audioTest.channels}s`); 177 | 178 | const inputDuration = audioTest.pcmData.length / audioTest.inRate / 2 / audioTest.channels; 179 | const outputDuration = res.length / audioTest.outRate / Float32Array.BYTES_PER_ELEMENT / audioTest.channels; 180 | assert(Math.abs(inputDuration - outputDuration) < 0.01, `Stream duration not matching target, in: ${inputDuration}s != out:${outputDuration}`); 181 | console.log(); 182 | writeFileSync(path.resolve(__dirname, `../resources/${filename}_${audioTest.outRate}_${audioTest.quality || 7}_output.pcm`), res); 183 | } 184 | } 185 | 186 | export const parallelTest = async () => { 187 | console.log('================='); 188 | console.log('Parallel Test'); 189 | console.log('================='); 190 | 191 | const start = performance.now(); 192 | const results = await Promise.all(audioTests.map(async (audioTest) => { 193 | const resampler = new SoxrResamplerThread( 194 | audioTest.channels, 195 | audioTest.inRate, 196 | audioTest.outRate, 197 | SoxrDatatype.SOXR_INT16, 198 | SoxrDatatype.SOXR_INT16, 199 | audioTest.quality, 200 | ); 201 | await resampler.init(); 202 | const filename = path.parse(audioTest.inFile).name; 203 | 204 | const res = Buffer.concat([ 205 | await resampler.processChunk(audioTest.pcmData), 206 | await resampler.processChunk(null) 207 | ]); 208 | const inputDuration = audioTest.pcmData.length / audioTest.inRate / 2 / audioTest.channels; 209 | const outputDuration = res.length / audioTest.outRate / 2 / audioTest.channels; 210 | assert(Math.abs(inputDuration - outputDuration) < 0.01, `Stream duration not matching target, in: ${inputDuration}s != out:${outputDuration}`); 211 | resampler.destroy(); 212 | // writeFileSync(path.resolve(__dirname, `../resources/${filename}_${audioTest.outRate}_${audioTest.quality || 7}_output.pcm`), res); 213 | return res; 214 | })); 215 | const end = performance.now(); 216 | console.log(`Resampled all ${audioTests.length} files in ${Math.floor(end - start)}ms`); 217 | // console.log(results); 218 | } 219 | 220 | const main = async () => { 221 | await promiseBasedTest(); 222 | await streamBasedTest(); 223 | await smallChunksTest(); 224 | await inBufferTest(); 225 | await typeChangeTest(); 226 | await parallelTest(); 227 | }; 228 | 229 | main().catch((e) => { 230 | console.error(e); 231 | process.exit(1); 232 | }) 233 | -------------------------------------------------------------------------------- /src/test_utils.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { readFileSync } from 'fs'; 3 | import { SoxrQuality } from './utils'; 4 | 5 | const audioTestsDef = [ 6 | // {inFile: path.resolve(__dirname, `../resources/24000hz_mono_test.pcm`), inRate: 24000, outRate: 48000, channels: 1}, 7 | // {inFile: path.resolve(__dirname, `../resources/24000hz_mono_test.pcm`), inRate: 24000, outRate: 48000, channels: 1, quality: SoxrQuality.SOXR_LQ}, 8 | // {inFile: path.resolve(__dirname, `../resources/24000hz_mono_test.pcm`), inRate: 24000, outRate: 48000, channels: 1, quality: SoxrQuality.SOXR_MQ}, 9 | // {inFile: path.resolve(__dirname, `../resources/24000hz_test.pcm`), inRate: 24000, outRate: 24000, channels: 2}, 10 | // {inFile: path.resolve(__dirname, `../resources/24000hz_test.pcm`), inRate: 24000, outRate: 44100, channels: 2}, 11 | {inFile: path.resolve(__dirname, `../resources/44100hz_test.pcm`), inRate: 44100, outRate: 48000, channels: 2, quality: SoxrQuality.SOXR_LQ}, 12 | {inFile: path.resolve(__dirname, `../resources/44100hz_test.pcm`), inRate: 44100, outRate: 48000, channels: 2, quality: SoxrQuality.SOXR_MQ}, 13 | {inFile: path.resolve(__dirname, `../resources/44100hz_test.pcm`), inRate: 44100, outRate: 48000, channels: 2, quality: SoxrQuality.SOXR_HQ}, 14 | {inFile: path.resolve(__dirname, `../resources/44100hz_test.pcm`), inRate: 44100, outRate: 48000, channels: 2, quality: SoxrQuality.SOXR_VHQ}, 15 | // {inFile: path.resolve(__dirname, `../resources/44100hz_test.pcm`), inRate: 44100, outRate: 24000, channels: 2}, 16 | ]; 17 | export const audioTests = audioTestsDef.map((test) => ({ 18 | ...test, 19 | pcmData: readFileSync(test.inFile), 20 | })); 21 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export enum SoxrDatatype { 4 | SOXR_FLOAT32 = 0, 5 | SOXR_FLOAT64 = 1, 6 | SOXR_INT32 = 2, 7 | SOXR_INT16 = 3, 8 | }; 9 | 10 | export enum SoxrQuality { 11 | SOXR_QQ = 0, 12 | SOXR_LQ = 1, 13 | SOXR_MQ = 2, 14 | SOXR_HQ = 4, 15 | SOXR_VHQ = 6, 16 | } 17 | 18 | export const bytesPerDatatypeSample = { 19 | [SoxrDatatype.SOXR_FLOAT32]: 4, 20 | [SoxrDatatype.SOXR_FLOAT64]: 8, 21 | [SoxrDatatype.SOXR_INT32]: 4, 22 | [SoxrDatatype.SOXR_INT16]: 2, 23 | }; 24 | 25 | export interface EmscriptenModuleSoxr extends EmscriptenModule { 26 | _soxr_create( 27 | inputRate: number, 28 | outputRate: number, 29 | num_channels: number, 30 | errPtr: number, 31 | ioSpecPtr: number, 32 | qualitySpecPtr: number, 33 | runtimeSpecPtr: number, 34 | ): number; 35 | _soxr_delete(resamplerPtr: number): void; 36 | _soxr_process( 37 | resamplerPtr: number, 38 | inBufPtr: number, 39 | inLen: number, 40 | inConsummedLenPtr: number, 41 | outBufPtr: number, 42 | outLen: number, 43 | outEmittedLenPtr: number, 44 | ): number; 45 | _soxr_io_spec( 46 | ioSpecPtr: number, 47 | itype: number, 48 | otype: number, 49 | ): void; 50 | _soxr_quality_spec(qualitySpecPtr: number, recipe: number, flags: number): void; 51 | _soxr_delay(ioSpecPtr: number): number; 52 | _sizeof_soxr_io_spec_t(): number; 53 | _sizeof_soxr_quality_spec_t(): number; 54 | 55 | getValue(ptr: number, type: string): any; 56 | setValue(ptr: number, value: any, type: string): any; 57 | AsciiToString(ptr: number): string; 58 | } 59 | 60 | export const memoize = any>(fct: T) : T => { 61 | let res: ReturnType = null; 62 | const resolver = (...args) => { 63 | if (res) { 64 | return res; 65 | } 66 | res = fct(...args); 67 | return res; 68 | }; 69 | return resolver as T; 70 | } 71 | 72 | export const limitConcurrency = Promise>(fct: T): T => { 73 | let currentCall = null; 74 | const resolver = async (...args) => { 75 | if (currentCall) { 76 | await currentCall; 77 | } 78 | currentCall = fct(...args); 79 | const res = await currentCall; 80 | currentCall = null; 81 | return res; 82 | }; 83 | return resolver as T; 84 | } 85 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "allowSyntheticDefaultImports": true, 6 | "target": "ES2019", 7 | "noImplicitAny": false, 8 | "moduleResolution": "node", 9 | "allowJs": true, 10 | "outDir": "app", 11 | "baseUrl": "src", 12 | "resolveJsonModule": true, 13 | "skipLibCheck": true, 14 | "declaration": true, 15 | 16 | "sourceRoot": "/", 17 | "inlineSources": true, 18 | "inlineSourceMap": true 19 | }, 20 | "include": [ 21 | "src/**/*" 22 | ] 23 | } 24 | --------------------------------------------------------------------------------