mirror of
https://github.com/k2-fsa/sherpa-onnx.git
synced 2026-11-11 05:20:52 +08:00
169 lines
6.4 KiB
JavaScript
169 lines
6.4 KiB
JavaScript
// Copyright (c) 2026 Xiaomi Corporation
|
|
|
|
const assert = require('assert');
|
|
const fs = require('fs');
|
|
const { MessageChannel } = require('worker_threads');
|
|
const sherpa_onnx = require('../lib/sherpa-onnx.js');
|
|
const addon = require('../lib/addon.js');
|
|
|
|
/** Round-trip mono readWave/writeWave and readWaveFromBinary. */
|
|
function testMonoWaveIO() {
|
|
const samples = new Float32Array(1600);
|
|
for (let i = 0; i < samples.length; ++i) {
|
|
samples[i] = 0.5 * Math.sin(i / 10);
|
|
}
|
|
|
|
const filename = 'test-wave-io-mono.wav';
|
|
try {
|
|
assert.strictEqual(
|
|
sherpa_onnx.writeWave(filename, { samples, sampleRate: 16000 }), true);
|
|
for (const enableExternalBuffer of [true, false]) {
|
|
const wave = sherpa_onnx.readWave(filename, enableExternalBuffer);
|
|
assert.strictEqual(wave.sampleRate, 16000);
|
|
assert.strictEqual(wave.samples.length, 1600);
|
|
// 16-bit PCM quantization
|
|
for (let i = 0; i < samples.length; ++i) {
|
|
assert(Math.abs(wave.samples[i] - samples[i]) <= 2 / 32768);
|
|
}
|
|
}
|
|
|
|
const data = new Uint8Array(fs.readFileSync(filename));
|
|
// readWaveFromBinary is only exported by the raw addon.
|
|
const fromBinary = addon.readWaveFromBinary(data, false);
|
|
assert.strictEqual(fromBinary.sampleRate, 16000);
|
|
assert.strictEqual(fromBinary.samples.length, 1600);
|
|
} finally {
|
|
fs.unlinkSync(filename);
|
|
}
|
|
}
|
|
|
|
/** Round-trip multi-channel wave IO without loading a model. */
|
|
function testMultiChannelWaveIO() {
|
|
assert.strictEqual(typeof sherpa_onnx.readWaveMultiChannel, 'function');
|
|
assert.strictEqual(typeof sherpa_onnx.writeWaveMultiChannel, 'function');
|
|
|
|
const left = new Float32Array(1600);
|
|
const right = new Float32Array(1600);
|
|
for (let i = 0; i < left.length; ++i) {
|
|
left[i] = 0.9 * Math.sin(i / 10);
|
|
right[i] = -0.5 * Math.cos(i / 7);
|
|
}
|
|
|
|
const filename = 'test-wave-io-roundtrip.wav';
|
|
try {
|
|
assert.strictEqual(
|
|
sherpa_onnx.writeWaveMultiChannel(
|
|
filename, { samples: [left, right], sampleRate: 16000 }),
|
|
true);
|
|
for (const enableExternalBuffer of [true, false]) {
|
|
const wave =
|
|
sherpa_onnx.readWaveMultiChannel(filename, enableExternalBuffer);
|
|
assert.strictEqual(wave.sampleRate, 16000);
|
|
assert.deepStrictEqual(
|
|
wave.samples.map(channel => channel.length), [1600, 1600]);
|
|
// 16-bit PCM quantization
|
|
for (const [actual, expected] of [[wave.samples[0], left],
|
|
[wave.samples[1], right]]) {
|
|
for (let i = 0; i < expected.length; ++i) {
|
|
assert(Math.abs(actual[i] - expected[i]) <= 2 / 32768);
|
|
}
|
|
}
|
|
assert.notDeepStrictEqual(wave.samples[0], wave.samples[1]);
|
|
}
|
|
|
|
assert.strictEqual(
|
|
sherpa_onnx.writeWaveMultiChannel(
|
|
filename, { samples: [left], sampleRate: 16000 }),
|
|
true);
|
|
const mono = sherpa_onnx.readWaveMultiChannel(filename, false);
|
|
assert.strictEqual(mono.samples.length, 1);
|
|
assert.strictEqual(mono.samples[0].length, 1600);
|
|
|
|
// Any channel count is fine, not only 1 or 2.
|
|
assert.strictEqual(
|
|
sherpa_onnx.writeWaveMultiChannel(
|
|
filename, { samples: Array(3).fill(left), sampleRate: 16000 }),
|
|
true);
|
|
const three = sherpa_onnx.readWaveMultiChannel(filename, false);
|
|
assert.strictEqual(three.samples.length, 3);
|
|
|
|
// A wave with an empty data chunk has no samples per channel.
|
|
assert.strictEqual(
|
|
sherpa_onnx.writeWave(
|
|
filename, { samples: new Float32Array(0), sampleRate: 16000 }),
|
|
true);
|
|
for (const enableExternalBuffer of [true, false]) {
|
|
const empty =
|
|
sherpa_onnx.readWaveMultiChannel(filename, enableExternalBuffer);
|
|
assert.strictEqual(empty.samples.length, 1);
|
|
assert.strictEqual(empty.samples[0].length, 0);
|
|
}
|
|
} finally {
|
|
fs.unlinkSync(filename);
|
|
}
|
|
|
|
for (const obj of [undefined, null, 1, [], { samples: [left] },
|
|
{ samples: [left], sampleRate: '16000' }]) {
|
|
assert.throws(
|
|
() => sherpa_onnx.writeWaveMultiChannel(filename, obj), TypeError);
|
|
}
|
|
for (const samples of [[], [new Float32Array(0)],
|
|
[new Float32Array(10), new Float32Array(11)]]) {
|
|
assert.throws(
|
|
() => sherpa_onnx.writeWaveMultiChannel(
|
|
filename, { samples, sampleRate: 16000 }),
|
|
RangeError);
|
|
}
|
|
for (const samples of [[new Int16Array(10)], [left, 1], ['a']]) {
|
|
assert.throws(
|
|
() => sherpa_onnx.writeWaveMultiChannel(
|
|
filename, { samples, sampleRate: 16000 }),
|
|
TypeError);
|
|
}
|
|
for (const sampleRate of [0, -1, NaN, Infinity, 44100.5, 2 ** 31]) {
|
|
assert.throws(
|
|
() => sherpa_onnx.writeWaveMultiChannel(
|
|
filename, { samples: [left], sampleRate }),
|
|
RangeError);
|
|
}
|
|
|
|
// A channel getter may detach an earlier channel's ArrayBuffer; the saved
|
|
// pointers must not be read after any property getter has run.
|
|
{
|
|
const first = new Float32Array(10);
|
|
const channels = [first];
|
|
const { port1, port2 } = new MessageChannel();
|
|
try {
|
|
Object.defineProperty(channels, 1, {
|
|
get() {
|
|
port1.postMessage(first.buffer, [first.buffer]);
|
|
return new Float32Array(10);
|
|
},
|
|
});
|
|
assert.throws(
|
|
() => sherpa_onnx.writeWaveMultiChannel(
|
|
filename, { samples: channels, sampleRate: 16000 }),
|
|
RangeError);
|
|
} finally {
|
|
port1.close();
|
|
port2.close();
|
|
}
|
|
}
|
|
assert.throws(() => sherpa_onnx.readWaveMultiChannel(), TypeError);
|
|
assert.throws(() => sherpa_onnx.readWaveMultiChannel(1), TypeError);
|
|
assert.throws(
|
|
() => sherpa_onnx.readWaveMultiChannel('no-such-file.wav'), TypeError);
|
|
assert.throws(
|
|
() => sherpa_onnx.readWaveMultiChannel(filename, 1), TypeError);
|
|
assert.throws(
|
|
() => sherpa_onnx.readWaveMultiChannel(filename, true, true), TypeError);
|
|
}
|
|
|
|
function main() {
|
|
testMonoWaveIO();
|
|
testMultiChannelWaveIO();
|
|
console.log('Wave IO tests passed');
|
|
}
|
|
|
|
main();
|