@@ -5,23 +5,20 @@ import { join } from "node:path";
55import { fileURLToPath } from "node:url" ;
66import { afterEach , describe , expect , it } from "vitest" ;
77import { SHERPA_ERROR_PREFIX , SHERPA_RESULT_PREFIX , SHERPA_WINDOW_PREFIX } from "./parakeet.js" ;
8+ import { encodeWav } from "./wav.test-helpers.js" ;
89
910const WORKER = fileURLToPath ( new URL ( "./sherpaWorker.ts" , import . meta. url ) ) ;
1011
1112// Stand-in sherpa-onnx-node, 3 s of sound: the whole window (300 samples) drops the first 2 s; the
1213// padded window (350) hears it, and for speech.wav so does the padded gap +-0.5 s (308) alone.
1314const FAKE_SHERPA = `
14- let path;
15+ const path = JSON.parse(process.env.HYPERFRAMES_PARAKEET_INPUT).wavPath ;
1516module.exports = {
16- readWave(p) {
17- path = p;
18- if (path.endsWith("broken.wav")) throw new Error("Failed to read " + path);
19- if (path.endsWith("lines.wav")) throw new Error("Could not find it. Tried\\n\\n ../a.node\\n ./b.node\\n");
20- if (path.endsWith("long.wav")) return { sampleRate: 100, samples: new Float32Array(60000) };
21- return { sampleRate: 100, samples: new Float32Array(300).fill(0.5) };
22- },
17+ // What Electron's V8 sandbox throws for this reader's native buffer: the worker reads the WAV itself.
18+ readWave() { throw new Error("External buffers are not allowed"); },
2319 OfflineRecognizer: class {
2420 constructor() {
21+ if (path.endsWith("lines.wav")) throw new Error("Could not find it. Tried\\n\\n ../a.node\\n ./b.node\\n");
2522 if (process.env.STARTED) require("fs").writeFileSync(process.env.STARTED, "");
2623 }
2724 createStream() { return { acceptWaveform(w) { this.w = w; } }; }
@@ -69,21 +66,28 @@ describe("sherpaWorker", () => {
6966 return root ;
7067 }
7168
69+ /** 3 s of steady sound at 100 Hz, as the stand-in decoder expects; long.wav is 600 s of silence. */
70+ function wav ( name : string ) : string {
71+ const samples = name === "long.wav" ? new Float32Array ( 60000 ) : new Float32Array ( 300 ) . fill ( 0.5 ) ;
72+ writeFileSync ( join ( root , name ) , encodeWav ( samples , 100 ) ) ;
73+ return join ( root , name ) ;
74+ }
75+
7276 function windowsOf ( stdout : string ) {
7377 const line = stdout . split ( "\n" ) . find ( ( l ) => l . startsWith ( SHERPA_RESULT_PREFIX ) ) ! ;
7478 return JSON . parse ( line . slice ( SHERPA_RESULT_PREFIX . length ) ) ;
7579 }
7680
7781 it ( "decodes a gap that skipped loud audio on its own and splices in its new tokens" , async ( ) => {
78- const { code, stdout } = await runWorker ( fakeRuntime ( ) , "speech.wav" ) ;
82+ const { code, stdout } = await runWorker ( fakeRuntime ( ) , wav ( "speech.wav" ) ) ;
7983 expect ( code ) . toBe ( 0 ) ;
8084 expect ( windowsOf ( stdout ) ) . toEqual ( [
8185 { offset : 0 , tokens : [ " ask" , " not" ] , timestamps : [ 0.5 , 2.08 ] , durations : [ 0.4 , 0.4 ] } ,
8286 ] ) ;
8387 } ) ;
8488
8589 it ( "prints each window as it is decoded, with the audio seconds done" , async ( ) => {
86- const { stdout } = await runWorker ( fakeRuntime ( ) , "speech.wav" ) ;
90+ const { stdout } = await runWorker ( fakeRuntime ( ) , wav ( "speech.wav" ) ) ;
8791 const streamed = stdout
8892 . split ( "\n" )
8993 . filter ( ( l ) => l . startsWith ( SHERPA_WINDOW_PREFIX ) )
@@ -92,17 +96,19 @@ describe("sherpaWorker", () => {
9296 } ) ;
9397
9498 it ( "re-decodes the window with leading silence when the gap alone gives nothing" , async ( ) => {
95- const { code, stdout } = await runWorker ( fakeRuntime ( ) , "hum.wav" ) ;
99+ const { code, stdout } = await runWorker ( fakeRuntime ( ) , wav ( "hum.wav" ) ) ;
96100 expect ( code ) . toBe ( 0 ) ;
97101 expect ( windowsOf ( stdout ) ) . toEqual ( [
98102 { offset : 0 , tokens : [ " ask" , " not" ] , timestamps : [ 0.25 , 1 ] , durations : [ 0.4 , 0.4 ] } ,
99103 ] ) ;
100104 } ) ;
101105
102106 it ( "exits non-zero with one prefixed error line when it cannot read the audio" , async ( ) => {
103- const { code, stderr } = await runWorker ( fakeRuntime ( ) , "broken.wav" ) ;
107+ const broken = join ( fakeRuntime ( ) , "broken.wav" ) ;
108+ writeFileSync ( broken , "not audio" ) ;
109+ const { code, stderr } = await runWorker ( root , broken ) ;
104110 expect ( code ) . toBe ( 1 ) ;
105- expect ( stderr ) . toContain ( `${ SHERPA_ERROR_PREFIX } Failed to read broken.wav ` ) ;
111+ expect ( stderr ) . toContain ( `${ SHERPA_ERROR_PREFIX } ${ broken } is not a 16-bit mono PCM WAV ` ) ;
106112 } ) ;
107113
108114 it ( "keeps every line of a multi-line error on its one prefixed line" , async ( ) => {
@@ -116,7 +122,7 @@ describe("sherpaWorker", () => {
116122 async ( ) => {
117123 const started = join ( fakeRuntime ( ) , "started" ) ;
118124 const input = JSON . stringify ( {
119- wavPath : "long.wav" ,
125+ wavPath : wav ( "long.wav" ) ,
120126 runtimePath : join ( root , "node_modules" , "sherpa-onnx-node" , "index.js" ) ,
121127 config : { } ,
122128 } ) ;
0 commit comments