@gjsify/speech-recognition 0.0.0-stage → 0.64.33-next.323
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -2
- package/globals.mjs +26 -0
- package/lib/esm/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/backend.js +1 -0
- package/lib/esm/events.js +1 -0
- package/lib/esm/fake-engine.js +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/model-manifest.js +1 -0
- package/lib/esm/models.js +1 -0
- package/lib/esm/recognition.js +1 -0
- package/lib/esm/register.js +1 -0
- package/lib/esm/results.js +1 -0
- package/lib/types/backend.d.ts +44 -0
- package/lib/types/events.d.ts +33 -0
- package/lib/types/fake-engine.d.ts +18 -0
- package/lib/types/gst-source.spec.d.ts +2 -0
- package/lib/types/index.d.ts +13 -0
- package/lib/types/model-manifest.d.ts +25 -0
- package/lib/types/models.d.ts +37 -0
- package/lib/types/models.spec.d.ts +2 -0
- package/lib/types/real-engine.spec.d.ts +2 -0
- package/lib/types/recognition.d.ts +58 -0
- package/lib/types/recognition.spec.d.ts +2 -0
- package/lib/types/register.d.ts +1 -0
- package/lib/types/results.d.ts +41 -0
- package/lib/types/results.spec.d.ts +2 -0
- package/package.json +93 -5
package/README.md
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @gjsify/speech-recognition
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
W3C Web Speech `SpeechRecognition` for GJS, recognized **on this machine**: the streaming zipformer of [sherpa-onnx](https://github.com/k2-fsa/sherpa-onnx) through [`@gjsify/sherpa-native`](../sherpa-native#readme), with speech boundaries from [`@gjsify/vad`](../vad#readme) and audio from [`@gjsify/media-capture`](../media-capture#readme). Nothing talks to a recognition service. Linux x64 only today. Tier 3 (experimental, [ADR 0003](../../../docs/adr/0003-package-tiering.md)) until promoted. This is step 3 of [ADR 0127](../../../docs/adr/0127-speech-recognition-on-local-engines.md).
|
|
4
|
+
|
|
5
|
+
Part of the [gjsify](https://github.com/gjsify/gjsify) project — Node.js and Web APIs for GJS (GNOME JavaScript).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gjsify install @gjsify/speech-recognition
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
The model is not bundled. `available()` and `install()` are the spec's on-device additions (Web Speech draft of 27 Oct 2026):
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import '@gjsify/speech-recognition/register';
|
|
19
|
+
|
|
20
|
+
const options = { langs: ['de-DE'], processLocally: true };
|
|
21
|
+
if ((await SpeechRecognition.available(options)) === 'downloadable') {
|
|
22
|
+
await SpeechRecognition.install(options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const recognition = new SpeechRecognition();
|
|
26
|
+
recognition.lang = 'de-DE';
|
|
27
|
+
recognition.interimResults = true;
|
|
28
|
+
recognition.continuous = true;
|
|
29
|
+
recognition.onresult = (event) => {
|
|
30
|
+
const result = event.results[event.resultIndex];
|
|
31
|
+
console.log(result.isFinal ? 'final:' : 'interim:', result[0].transcript);
|
|
32
|
+
};
|
|
33
|
+
recognition.onerror = (event) => console.warn(event.error, event.message);
|
|
34
|
+
recognition.start(); // default capture device, 16 kHz mono
|
|
35
|
+
// …
|
|
36
|
+
recognition.stop(); // delivers the pending utterance, then `end`
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Without `/register`, import the classes: `import { SpeechRecognition } from '@gjsify/speech-recognition'`.
|
|
40
|
+
|
|
41
|
+
## Surface
|
|
42
|
+
|
|
43
|
+
| Member | Behavior here |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `start()` / `stop()` / `abort()` | per spec; `start()` while running throws `InvalidStateError`. `stop()` still delivers the pending utterance, `abort()` drops it |
|
|
46
|
+
| `start(track)` | the track is validated, but a real `MediaStreamTrack` fires `audio-capture` (not supported yet) |
|
|
47
|
+
| `lang`, `continuous`, `interimResults`, `maxAlternatives` | per spec. In continuous mode later transcripts are space-prefixed |
|
|
48
|
+
| `processLocally` | **always `true`**; setting `false` throws `NotSupportedError`. The spec default is `false`; ADR 0127 decision 3 overrides it, since there is no remote engine to fall back to |
|
|
49
|
+
| `phrases` | a plain array of `SpeechRecognitionPhrase` (`boost` in [0, 10]). The zipformer has no hotwords, so a non-empty list fires error `phrases-not-supported` |
|
|
50
|
+
| `grammars`, `serviceURI` | not implemented |
|
|
51
|
+
| `SpeechRecognition.available({ langs, processLocally })` | worst status over `langs`: `available` < `downloading` < `downloadable` < `unavailable`. A missing **engine** is `unavailable`, so a prompt never fetches a model for nothing. Invalid BCP 47 rejects with `SyntaxError` |
|
|
52
|
+
| `SpeechRecognition.install({ langs, processLocally })` | `true` once cached; `false` for empty or unsupported `langs`. `processLocally` is ignored |
|
|
53
|
+
| events | `start`, `audiostart`, `soundstart`, `speechstart`, `speechend`, `soundend`, `audioend`, `result`, `nomatch`, `error`, `end` |
|
|
54
|
+
|
|
55
|
+
Errors use the spec vocabulary plus `language-not-supported` (no model for `lang`), `phrases-not-supported`, `audio-capture` and `service-not-allowed`.
|
|
56
|
+
|
|
57
|
+
`hasRecognitionBackend()` / `describeRecognitionBackend()` say whether, and why not, an engine is there. `createFakeRecognizerEngine()` and the `engine` / `vad` / `source` members of `SpeechRecognitionInit` let an app test its handling without a model, a microphone or sound.
|
|
58
|
+
|
|
59
|
+
## Finalizing
|
|
60
|
+
|
|
61
|
+
The VAD (300 ms of silence) is the primary finalizer, so a final result lands roughly 300 ms after the speaker stops plus decoding. When the VAD model is not cached, the zipformer's own endpoint detection finalizes instead, which is later and less steady.
|
|
62
|
+
|
|
63
|
+
## Latency budget and engine candidates
|
|
64
|
+
|
|
65
|
+
[Lotse](https://github.com/JumpLink/lotse) needs a spoken conversation mode with **500 ms from end of speech to text**. Its measurements on an i9-10900K / RX 6600 (2026-10-28 to 30), as recorded in ADR 0127:
|
|
66
|
+
|
|
67
|
+
| Engine / model | WER | end-to-end | footprint |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| whisper.cpp `large-v3-turbo` q5_0, Vulkan, GPU idle | 7.3 % / 10.8 % | 428 / 457 ms | 548 MB model |
|
|
70
|
+
| the same, GPU computing | 7.3 % / 10.8 % | 1170 / 1420 ms | 548 MB model |
|
|
71
|
+
| sherpa-onnx `parakeet-tdt-0.6b-v3` int8, CPU | 12.0 % / 13.1 % | 239 / 339 ms | 641 MB, 1.1 GB RSS |
|
|
72
|
+
| sherpa-onnx streaming `zipformer-de-kroko`, CPU | 15.3 % / 16.5 % | 32 / 53 ms | 68 MB |
|
|
73
|
+
| whisper.cpp CPU, `small` and up | — | 2–18 s | misses the budget |
|
|
74
|
+
|
|
75
|
+
This package ships the **streaming zipformer** row: it is the only candidate that yields interim results and needs no GPU. Measured for `zipformer-de-kroko`: about **32 ms after the end of speech**, 183 MB RSS, CPU only. The whisper-native backend (accurate finals) and the machine-fit choice between backends are later ADR 0127 steps and not part of this package.
|
|
76
|
+
|
|
77
|
+
## Model
|
|
78
|
+
|
|
79
|
+
`sherpa-onnx-streaming-zipformer-de-kroko-2025-08-06`, four files fetched through [`@gjsify/model-store`](../model-store#readme) (size and SHA-256 verified before a file reaches its place), cached under `models/speech/`. The licence is Kroko's community licence (advertised as CC-BY-SA; the model card says `license: other`), so the entry is marked `redistributable: false` — gjsify downloads it from upstream and never bundles it.
|
|
80
|
+
|
|
81
|
+
## Tests
|
|
82
|
+
|
|
83
|
+
No test plays or records sound. The session logic runs on a fake engine and a fake VAD; the GStreamer path reads `audiotestsrc`/`filesrc` into a `fakesink`-style reader. The real-engine suite skips unless the prebuild and the cached model are there, and no CI job downloads a model. Accuracy is asserted as "the final contains these words", never an exact string.
|
|
84
|
+
|
|
85
|
+
| Variable | Effect |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `GJSIFY_SPEECHREC_TEST_WAV` | a 16 kHz mono WAV with speech, for the real-engine suite |
|
|
88
|
+
| `GJSIFY_SPEECHREC_EXPECT` | comma-separated words the final must contain |
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT
|
package/globals.mjs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports the browser's own Web Speech recognition globals; the sherpa-onnx GJS backend
|
|
3
|
+
* stays GJS-only.
|
|
4
|
+
*
|
|
5
|
+
* `@gjsify/resolve-npm/runtime-aliases.mjs` routes `@gjsify/speech-recognition` here whenever
|
|
6
|
+
* `gjsify.runtimes.<target> === "native"`, so every root export a consumer may import must
|
|
7
|
+
* be answerable from here or the routed bundle dies with `MISSING_EXPORT`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const SpeechRecognition = globalThis.SpeechRecognition ?? globalThis.webkitSpeechRecognition;
|
|
11
|
+
export const SpeechRecognitionEvent = globalThis.SpeechRecognitionEvent;
|
|
12
|
+
export const SpeechRecognitionErrorEvent = globalThis.SpeechRecognitionErrorEvent;
|
|
13
|
+
export const SpeechRecognitionResult = globalThis.SpeechRecognitionResult;
|
|
14
|
+
export const SpeechRecognitionResultList = globalThis.SpeechRecognitionResultList;
|
|
15
|
+
export const SpeechRecognitionAlternative = globalThis.SpeechRecognitionAlternative;
|
|
16
|
+
export const SpeechRecognitionPhrase = globalThis.SpeechRecognitionPhrase;
|
|
17
|
+
|
|
18
|
+
/** Whether this host has a recognizer at all. In a browser: the browser's own. */
|
|
19
|
+
export async function hasSpeechRecognitionBackend() {
|
|
20
|
+
return typeof SpeechRecognition === 'function';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Which backend recognizes, in words. In a browser: the browser's own. */
|
|
24
|
+
export async function describeSpeechRecognitionBackend() {
|
|
25
|
+
return (await hasSpeechRecognitionBackend()) ? 'browser (native SpeechRecognition)' : 'none';
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{getLoadError as e,hasNativeSherpa as t,loadNativeSherpa as n}from"@gjsify/sherpa-native";import{hostOs as r}from"@gjsify/utils/core";function hasRecognitionBackend(){let e=r();return e===`darwin`||e===`win32`?!1:t()}function describeRecognitionBackend(){if(!hasRecognitionBackend()){let t=e();return`none: @gjsify/sherpa-native is not available on this host${t?` (${t.message})`:``}`}let t=n();return`sherpa-onnx ${t.version()} on onnxruntime ${t.onnxruntime_version()} (@gjsify/sherpa-native)`}function createSherpaEngine(e){let t=n();if(t===null)throw Error(describeRecognitionBackend());let r=new t.OnlineRecognizer({encoder:e.encoder,decoder:e.decoder,joiner:e.joiner,tokens:e.tokens,sample_rate:e.sampleRate,num_threads:e.numThreads??1,enable_endpoint:!0}),i=null,feed=t=>{let n=i;for(n.accept(e.sampleRate,t);n.is_ready();)n.decode();return{text:n.get_text(),endpoint:n.is_endpoint()}};return{open(){r.open(),i??=r.create_stream(null)},accept:feed,flushTail(){return feed(new Float32Array(Math.round(e.sampleRate*.5)))},detail(){try{let e=JSON.parse(i.get_json());return{tokens:e.tokens??[],timestamps:e.timestamps??[],logProbs:e.ys_probs??[]}}catch{return{tokens:[],timestamps:[],logProbs:[]}}},reset(){i.reset()},close(){i?.close(),i=null,r.close()}}}export{createSherpaEngine,describeRecognitionBackend,hasRecognitionBackend};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{SpeechRecognitionResultList as e}from"./results.js";import{Event as t}from"@gjsify/dom-events";var SpeechRecognitionEvent=class extends t{resultIndex;results;constructor(t,n={}){super(t,n),this.resultIndex=n.resultIndex??0,this.results=n.results??new e}get[Symbol.toStringTag](){return`SpeechRecognitionEvent`}},SpeechRecognitionErrorEvent=class extends t{error;message;constructor(e,t){super(e,t),this.error=t.error,this.message=t.message??``}get[Symbol.toStringTag](){return`SpeechRecognitionErrorEvent`}};function defineEventHandler(e,t){let n=Symbol(`on${t}`);Object.defineProperty(e,`on${t}`,{configurable:!0,enumerable:!0,get(){return this[n]??null},set(e){this[n]&&this.removeEventListener(t,this[n]),this[n]=typeof e==`function`?e:null,this[n]&&this.addEventListener(t,this[n])}})}export{SpeechRecognitionErrorEvent,SpeechRecognitionEvent,defineEventHandler};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function createFakeRecognizerEngine(){let e=[],t=0,n={text:``},r=null,i={tokens:[],timestamps:[],logProbs:[]},a={samples:0,resets:0,tails:0,opened:!1,closed:!1},frame=e=>({text:e.text,endpoint:e.endpoint??!1});return{script(n){e=[...n],t=0},setTailText(e){r=e},setDetail(e){i={tokens:[],timestamps:[],logProbs:[],...e}},get samplesAccepted(){return a.samples},get resets(){return a.resets},get tailFlushes(){return a.tails},get opened(){return a.opened},get closed(){return a.closed},open(){a.opened=!0},accept(r){return a.samples+=r.length,t<e.length&&(n=e[t++]),frame(n)},flushTail(){return a.tails++,frame({text:r??n.text})},detail:()=>i,reset(){a.resets++,n={text:``},r=null},close(){a.closed=!0}}}export{createFakeRecognizerEngine};
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createSherpaEngine as e,describeRecognitionBackend as t,hasRecognitionBackend as n}from"./backend.js";import{SpeechRecognitionAlternative as r,SpeechRecognitionPhrase as i,SpeechRecognitionResult as a,SpeechRecognitionResultList as o}from"./results.js";import{SpeechRecognitionErrorEvent as s,SpeechRecognitionEvent as c}from"./events.js";import{createFakeRecognizerEngine as l}from"./fake-engine.js";import{DEFAULT_RECOGNITION_MODEL as u,RECOGNITION_MODELS as d,findRecognitionModel as f,modelFor as p}from"./model-manifest.js";import{available as m,install as h,isModelCached as g,modelCacheDir as _,uninstall as v}from"./models.js";import{SpeechRecognition as y}from"./recognition.js";export{u as DEFAULT_RECOGNITION_MODEL,d as RECOGNITION_MODELS,y as SpeechRecognition,r as SpeechRecognitionAlternative,s as SpeechRecognitionErrorEvent,c as SpeechRecognitionEvent,i as SpeechRecognitionPhrase,a as SpeechRecognitionResult,o as SpeechRecognitionResultList,m as available,l as createFakeRecognizerEngine,e as createSherpaEngine,t as describeRecognitionBackend,f as findRecognitionModel,n as hasRecognitionBackend,h as install,g as isModelCached,_ as modelCacheDir,p as modelFor,v as uninstall};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";const e=[`command`,`dictation`,`conversation`],t=`zipformer-de-kroko`,n=`https://huggingface.co/csukuangfj/sherpa-onnx-streaming-zipformer-de-kroko-2025-08-06/resolve/main`,r=[{id:`zipformer-de-kroko`,files:[{file:`encoder.onnx`,url:`${n}/encoder.onnx`,bytes:70091557,sha256:`6e83993d6967ec7a3498b055b7e85ace85b5d64d1b1e8773cb29a43a11f5edb5`},{file:`decoder.onnx`,url:`${n}/decoder.onnx`,bytes:617489,sha256:`94a29592b403c53fa2231b478637da1ab4abcef7f5e46e432098416a4a3ed562`},{file:`joiner.onnx`,url:`${n}/joiner.onnx`,bytes:336817,sha256:`28356bff070aea51ab1d725a3278e81d19f9300f860d3248a7014292264df15a`},{file:`tokens.txt`,url:`${n}/tokens.txt`,bytes:5606,sha256:`86e8370994ff2c01149ba8c4f8709aa93cdc18914b27a717e291e96faf39a6eb`}],license:`Kroko community model (advertised CC-BY-SA; the mirror declares none)`,redistributable:!1,description:`Kroko streaming zipformer, German (68 MB) — interim results on CPU, ~32 ms after end of speech.`,langs:[`de`],quality:`dictation`,sampleRate:16e3,hotwords:!1,encoder:`encoder.onnx`,decoder:`decoder.onnx`,joiner:`joiner.onnx`,tokens:`tokens.txt`}];function findRecognitionModel(e=t){return r.find(t=>t.id===e)}function modelFor(t,n=`command`){let i=t.split(`-`)[0].toLowerCase(),a=e.indexOf(n);return r.find(t=>t.langs.includes(i)&&e.indexOf(t.quality)>=a)}export{t as DEFAULT_RECOGNITION_MODEL,e as QUALITY_ORDER,r as RECOGNITION_MODELS,findRecognitionModel,modelFor};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{hasRecognitionBackend as e}from"./backend.js";import{syntaxError as t}from"./results.js";import{DEFAULT_RECOGNITION_MODEL as n,RECOGNITION_MODELS as r,modelFor as i}from"./model-manifest.js";import{ModelStore as a}from"@gjsify/model-store";const o=new a({capability:`speech`,models:r,defaultModel:n});function modelCacheDir(){return o.cacheDir}function resolveRecognitionModel(e){let t=o.locate(e.id);if(t===void 0)throw Error(`Recognition model '${e.id}' is not cached (${o.cachePath(e.id)}) — await SpeechRecognition.install() first.`);return t.files}function isModelCached(e){return o.locate(e.id)!==void 0}function validateLanguageTag(e){try{if(typeof e!=`string`||e===``)throw RangeError(`empty`);new Intl.Locale(e)}catch{throw t(`'${e}' is not a valid BCP 47 language tag`)}}const s={available:0,downloading:1,downloadable:2,unavailable:3};function statusFor(t,n){let r=i(t,n);return r===void 0||!e()?`unavailable`:o.availability(r.id)}async function available(e){let t=e?.langs;if(!Array.isArray(t))throw TypeError(`available(): options.langs must be an array of BCP 47 tags`);for(let e of t)validateLanguageTag(e);if(t.length===0)return`unavailable`;let n=e.quality??`command`;return t.map(e=>statusFor(e,n)).reduce((e,t)=>s[t]>s[e]?t:e,`available`)}async function install(t){let n=t?.langs;if(!Array.isArray(n))throw TypeError(`install(): options.langs must be an array of BCP 47 tags`);for(let e of n)validateLanguageTag(e);if(n.length===0)return!1;let r=t.quality??`command`,a=n.map(e=>i(e,r));if(a.some(e=>e===void 0)||!e())return!1;let{onProgress:s}=t;try{for(let e of new Set(a))await o.install(e.id,{signal:t.signal,fetch:t.fetchImpl,monitor:s===void 0?void 0:t=>t.addEventListener(`downloadprogress`,t=>{let{receivedBytes:n,totalBytes:r}=t;s({modelId:e.id,received:n,total:r})})});return!0}catch{return!1}}function uninstall(e=n){o.require(e);try{return o.uninstall(e)}catch{return!1}}export{available,install,isModelCached,modelCacheDir,resolveRecognitionModel,uninstall,validateLanguageTag};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{createSherpaEngine as e,hasRecognitionBackend as t}from"./backend.js";import{SpeechRecognitionAlternative as n,SpeechRecognitionResult as r,SpeechRecognitionResultList as i,namedError as a}from"./results.js";import{SpeechRecognitionErrorEvent as o,SpeechRecognitionEvent as s,defineEventHandler as c}from"./events.js";import{modelFor as l}from"./model-manifest.js";import{available as u,install as d,isModelCached as f,resolveRecognitionModel as p}from"./models.js";import{EventTarget as m}from"@gjsify/dom-events";import{openPcmSource as h}from"@gjsify/media-capture";import{available as g,createVad as _,toFloatSamples as v}from"@gjsify/vad";const y=[`audiostart`,`soundstart`,`speechstart`,`speechend`,`soundend`,`audioend`,`result`,`nomatch`,`error`,`start`,`end`],b=16e3;function systemLanguage(){try{return new Intl.DateTimeFormat().resolvedOptions().locale}catch{return`en-US`}}function mean(e){return e.length===0?0:e.reduce((e,t)=>e+t,0)/e.length}var SpeechRecognition=class extends m{lang=``;continuous=!1;interimResults=!1;maxAlternatives=1;unspokenPunctuation=!1;phrases=[];#e;#t=!1;#n=!1;#r=!1;#i=null;constructor(e={}){super(),this.#e=e}get processLocally(){return!0}set processLocally(e){if(!e)throw a(`SpeechRecognition.processLocally cannot be false: gjsify has no recognition service to reach`,`NotSupportedError`)}static available(e){return u(e)}static install(e){return d(e)}start(e){if(this.#t)throw a(`SpeechRecognition has already started`,`InvalidStateError`);let t=e;if(t!==void 0&&(t.kind!==`audio`||t.readyState!==`live`))throw a(`start(track) needs a live audio MediaStreamTrack`,`InvalidStateError`);this.#t=!0,this.#n=!1,this.#r=!1;let n={lang:this.lang||systemLanguage(),quality:`command`,continuous:this.continuous,interim:this.interimResults,phrases:this.phrases.length,track:t!==void 0};queueMicrotask(()=>{this.#s(n).catch(e=>{this.#o(`audio-capture`,e instanceof Error?e.message:String(e))})})}stop(){!this.#t||this.#n||(this.#n=!0,this.#i?.stop())}abort(){!this.#t||this.#r||(this.#n=!0,this.#r=!0,this.#i?.stop())}#a(e){this.dispatchEvent(new s(e))}#o(e,t){this.dispatchEvent(new o(`error`,{error:e,message:t})),this.#t&&(this.#t=!1,this.#a(`end`))}async#s(n){let r=l(n.lang,n.quality);if(r===void 0)return this.#o(`language-not-supported`,`No recognition model for '${n.lang}' at quality '${n.quality}'`);if(n.phrases>0&&!r.hotwords)return this.#o(`phrases-not-supported`,`Model '${r.id}' cannot be biased with phrases`);if(n.track)return this.#o(`audio-capture`,`start(track) is not supported yet — call start() for the default microphone`);let i;if(this.#e.engine!==void 0)i=this.#e.engine(r);else{if(!t()||!f(r))return this.#o(`service-not-allowed`,`Model '${r.id}' is not usable here — check SpeechRecognition.available() and install() it`);let n=p(r);i=e({encoder:n[r.encoder],decoder:n[r.decoder],joiner:n[r.joiner],tokens:n[r.tokens],sampleRate:r.sampleRate})}try{i.open()}catch(e){return this.#o(`service-not-allowed`,`The recognizer could not load: ${e instanceof Error?e.message:e}`)}let a=this.#c(),s;try{s=this.#e.source?.()??h({sampleRate:b,format:`S16LE`}),s.start?.()}catch(e){return i.close(),a?.close(),this.#o(`audio-capture`,e instanceof Error?e.message:String(e))}this.#i=s,this.#n&&s.stop(),this.#a(`start`);try{await this.#l(n,i,a,s)}catch(e){this.dispatchEvent(new o(`error`,{error:`audio-capture`,message:e instanceof Error?e.message:String(e)}))}finally{this.#i=null,s.stop(),a?.close(),i.close(),this.#t=!1,this.#a(`end`)}}#c(){try{if(this.#e.vad!==void 0){let e=this.#e.vad();return e?.open(),e}if(g()!==`available`)return null;let e=_({minSilenceMs:300});return e.open(),e}catch{return null}}async#l(e,t,a,c){let l=c.sampleRate??b,u=[],d=!1,f=!1,p=``,m=0,h=0,g=!1,list=e=>new i(e?[...u,e]:u),spaced=(e,t)=>t>0?` ${e}`:e,beginSound=()=>{f||(f=!0,this.#a(`soundstart`),this.#a(`speechstart`))},finalize=(i,a)=>{let o=(i?t.flushTail().text:a).trim(),c=t.detail();t.reset();let d=f;f&&(f=!1,this.#a(`speechend`),this.#a(`soundend`)),p=``;let _=h/l;if(h=m,o===``){d&&(this.#a(`nomatch`),e.continuous||(g=!0));return}let v=c.logProbs.map(e=>Math.exp(e)),y=c.timestamps,b=new r({alternatives:[new n(spaced(o,u.length),mean(v))],isFinal:!0,speechStartTime:_+(y.length>0?y[0]:0),speechEndTime:y.length>0?_+y[y.length-1]:m/l});u.push(b),this.dispatchEvent(new s(`result`,{resultIndex:u.length-1,results:list()})),e.continuous||(g=!0)},sawSpeechEnd=e=>e.some(e=>e.type===`speech-end`),sawSpeechStart=e=>e.some(e=>e.type===`speech-start`);for(;!g;){let i=await c.read();if(i===null||this.#r)break;d||(d=!0,this.#a(`audiostart`));let o=v(i,c.format??`S16LE`);m+=o.length;let f=a?a.accept(o):[],g=t.accept(o);if((sawSpeechStart(f)||g.text!==``)&&beginSound(),g.text!==``&&g.text!==p&&e.interim&&!g.endpoint){p=g.text;let e=new r({alternatives:[new n(spaced(g.text.trim(),u.length),0)],isFinal:!1,speechStartTime:h/l,speechEndTime:m/l});this.dispatchEvent(new s(`result`,{resultIndex:u.length,results:list(e)}))}sawSpeechEnd(f)?finalize(!0,g.text):g.endpoint&&finalize(!1,g.text)}if(!this.#r){if(!g&&d){a&&a.flush();let e=t.flushTail().text.trim();(f||e!==``)&&finalize(!1,e)}u.length===0&&this.dispatchEvent(new o(`error`,{error:`no-speech`,message:``}))}d&&this.#a(`audioend`)}};for(let e of y)c(SpeechRecognition.prototype,e);export{SpeechRecognition};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{SpeechRecognitionAlternative as e,SpeechRecognitionPhrase as t,SpeechRecognitionResult as n,SpeechRecognitionResultList as r}from"./results.js";import{SpeechRecognitionErrorEvent as i,SpeechRecognitionEvent as a}from"./events.js";import{SpeechRecognition as o}from"./recognition.js";const s=globalThis;s.SpeechRecognition===void 0&&(s.SpeechRecognition=o,s.SpeechRecognitionEvent=a,s.SpeechRecognitionErrorEvent=i,s.SpeechRecognitionResultList=r,s.SpeechRecognitionResult=n,s.SpeechRecognitionAlternative=e,s.SpeechRecognitionPhrase=t);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";var SpeechRecognitionAlternative=class{transcript;confidence;constructor(e,t=0){this.transcript=e,this.confidence=t}get[Symbol.toStringTag](){return`SpeechRecognitionAlternative`}},SpeechRecognitionResult=class{isFinal;speechStartTime;speechEndTime;length;constructor(e){this.isFinal=e.isFinal,this.speechStartTime=e.speechStartTime??0,this.speechEndTime=e.speechEndTime??0,this.length=e.alternatives.length,e.alternatives.forEach((e,t)=>{Object.defineProperty(this,t,{value:e,enumerable:!0})})}item(e){return this[e]}get[Symbol.toStringTag](){return`SpeechRecognitionResult`}},SpeechRecognitionResultList=class{length;constructor(e=[]){this.length=e.length,e.forEach((e,t)=>{Object.defineProperty(this,t,{value:e,enumerable:!0})})}item(e){return this[e]}[Symbol.iterator](){let e=0;return{next:()=>e<this.length?{value:this.item(e++),done:!1}:{value:void 0,done:!0}}}get[Symbol.toStringTag](){return`SpeechRecognitionResultList`}},SpeechRecognitionPhrase=class{phrase;boost;constructor(e,t=1){if(!(t>=0&&t<=10))throw syntaxError(`SpeechRecognitionPhrase boost must be within [0, 10], got ${t}`);this.phrase=String(e),this.boost=t}get[Symbol.toStringTag](){return`SpeechRecognitionPhrase`}};function syntaxError(e){return namedError(e,`SyntaxError`)}function namedError(e,t){let n=globalThis.DOMException;if(n)return new n(e,t);let r=Error(e);return r.name=t,r}export{SpeechRecognitionAlternative,SpeechRecognitionPhrase,SpeechRecognitionResult,SpeechRecognitionResultList,namedError,syntaxError};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Whether this host has a native recognizer. Quiet; a QUERY, not a guarantee — a model still has to be cached. */
|
|
2
|
+
export declare function hasRecognitionBackend(): boolean;
|
|
3
|
+
/** The recognizer stack in words, and — when there is none — why. For a settings panel or a bug report. */
|
|
4
|
+
export declare function describeRecognitionBackend(): string;
|
|
5
|
+
/** What the engine knows about the utterance it is decoding. */
|
|
6
|
+
export interface RecognizerFrame {
|
|
7
|
+
/** The hypothesis so far for the current utterance — interim, and it grows. */
|
|
8
|
+
text: string;
|
|
9
|
+
/** The model's own trailing-silence rule says the utterance ended. */
|
|
10
|
+
endpoint: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** The token-level detail of the current utterance; any field may be absent. */
|
|
13
|
+
export interface RecognizerDetail {
|
|
14
|
+
tokens: string[];
|
|
15
|
+
/** Seconds from the start of the current utterance. */
|
|
16
|
+
timestamps: number[];
|
|
17
|
+
/** Per-token log probabilities. */
|
|
18
|
+
logProbs: number[];
|
|
19
|
+
}
|
|
20
|
+
/** The seam the session talks to. */
|
|
21
|
+
export interface RecognizerEngine {
|
|
22
|
+
/** Load the model. Throws when it cannot be opened. */
|
|
23
|
+
open(): void;
|
|
24
|
+
/** Feed mono float samples at the engine's rate and decode everything that became ready. */
|
|
25
|
+
accept(samples: Float32Array): RecognizerFrame;
|
|
26
|
+
/** Pad the tail with silence so the last tokens are emitted, and decode. */
|
|
27
|
+
flushTail(): RecognizerFrame;
|
|
28
|
+
/** Token detail of the current utterance. Call before {@link reset}. */
|
|
29
|
+
detail(): RecognizerDetail;
|
|
30
|
+
/** Start a new utterance. */
|
|
31
|
+
reset(): void;
|
|
32
|
+
/** Release the model. Idempotent. */
|
|
33
|
+
close(): void;
|
|
34
|
+
}
|
|
35
|
+
export interface SherpaEngineConfig {
|
|
36
|
+
encoder: string;
|
|
37
|
+
decoder: string;
|
|
38
|
+
joiner: string;
|
|
39
|
+
tokens: string;
|
|
40
|
+
sampleRate: number;
|
|
41
|
+
numThreads?: number;
|
|
42
|
+
}
|
|
43
|
+
/** A {@link RecognizerEngine} over `GjsifySherpa.OnlineRecognizer`. */
|
|
44
|
+
export declare function createSherpaEngine(config: SherpaEngineConfig): RecognizerEngine;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Event } from '@gjsify/dom-events';
|
|
2
|
+
import { SpeechRecognitionResultList } from './results.js';
|
|
3
|
+
export interface SpeechRecognitionEventInit {
|
|
4
|
+
resultIndex?: number;
|
|
5
|
+
results?: SpeechRecognitionResultList;
|
|
6
|
+
bubbles?: boolean;
|
|
7
|
+
cancelable?: boolean;
|
|
8
|
+
composed?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare class SpeechRecognitionEvent extends Event {
|
|
11
|
+
/** The lowest index in `results` that changed. */
|
|
12
|
+
readonly resultIndex: number;
|
|
13
|
+
readonly results: SpeechRecognitionResultList;
|
|
14
|
+
constructor(type: string, init?: SpeechRecognitionEventInit);
|
|
15
|
+
get [Symbol.toStringTag](): string;
|
|
16
|
+
}
|
|
17
|
+
/** https://webaudio.github.io/web-speech-api/#enumdef-speechrecognitionerrorcode */
|
|
18
|
+
export type SpeechRecognitionErrorCode = 'no-speech' | 'aborted' | 'audio-capture' | 'network' | 'not-allowed' | 'service-not-allowed' | 'language-not-supported' | 'phrases-not-supported';
|
|
19
|
+
export interface SpeechRecognitionErrorEventInit extends SpeechRecognitionEventInit {
|
|
20
|
+
error: SpeechRecognitionErrorCode;
|
|
21
|
+
message?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class SpeechRecognitionErrorEvent extends Event {
|
|
24
|
+
readonly error: SpeechRecognitionErrorCode;
|
|
25
|
+
readonly message: string;
|
|
26
|
+
constructor(type: string, init: SpeechRecognitionErrorEventInit);
|
|
27
|
+
get [Symbol.toStringTag](): string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Install an `on<type>` IDL attribute on a prototype: assigning a function adds it as the
|
|
31
|
+
* listener, reassigning replaces it, `null` removes it.
|
|
32
|
+
*/
|
|
33
|
+
export declare function defineEventHandler(proto: object, type: string): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RecognizerDetail, RecognizerEngine } from './backend.js';
|
|
2
|
+
export interface FakeEngineStep {
|
|
3
|
+
text: string;
|
|
4
|
+
endpoint?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface FakeRecognizerEngine extends RecognizerEngine {
|
|
7
|
+
/** Frames the next `accept()` calls answer, one per call; the last repeats. */
|
|
8
|
+
script(steps: readonly FakeEngineStep[]): void;
|
|
9
|
+
/** What `flushTail()` answers (default: the last text). */
|
|
10
|
+
setTailText(text: string): void;
|
|
11
|
+
setDetail(detail: Partial<RecognizerDetail>): void;
|
|
12
|
+
readonly samplesAccepted: number;
|
|
13
|
+
readonly resets: number;
|
|
14
|
+
readonly tailFlushes: number;
|
|
15
|
+
readonly opened: boolean;
|
|
16
|
+
readonly closed: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function createFakeRecognizerEngine(): FakeRecognizerEngine;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { SpeechRecognition } from './recognition.js';
|
|
2
|
+
export type { PcmReader, SpeechRecognitionInit } from './recognition.js';
|
|
3
|
+
export { SpeechRecognitionErrorEvent, SpeechRecognitionEvent } from './events.js';
|
|
4
|
+
export type { SpeechRecognitionErrorCode, SpeechRecognitionErrorEventInit, SpeechRecognitionEventInit, } from './events.js';
|
|
5
|
+
export { SpeechRecognitionAlternative, SpeechRecognitionPhrase, SpeechRecognitionResult, SpeechRecognitionResultList, } from './results.js';
|
|
6
|
+
export { available, install, isModelCached, modelCacheDir, uninstall } from './models.js';
|
|
7
|
+
export type { SpeechRecognitionAvailability, SpeechRecognitionInstallOptions, SpeechRecognitionOptions, } from './models.js';
|
|
8
|
+
export { DEFAULT_RECOGNITION_MODEL, findRecognitionModel, modelFor, RECOGNITION_MODELS } from './model-manifest.js';
|
|
9
|
+
export type { RecognitionModelEntry, RecognitionQuality } from './model-manifest.js';
|
|
10
|
+
export { createSherpaEngine, describeRecognitionBackend, hasRecognitionBackend } from './backend.js';
|
|
11
|
+
export type { RecognizerDetail, RecognizerEngine, RecognizerFrame, SherpaEngineConfig } from './backend.js';
|
|
12
|
+
export { createFakeRecognizerEngine } from './fake-engine.js';
|
|
13
|
+
export type { FakeEngineStep, FakeRecognizerEngine } from './fake-engine.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ModelEntry } from '@gjsify/model-store';
|
|
2
|
+
/** The Web Speech `SpeechRecognitionQuality`, ordered. */
|
|
3
|
+
export type RecognitionQuality = 'command' | 'dictation' | 'conversation';
|
|
4
|
+
export declare const QUALITY_ORDER: readonly RecognitionQuality[];
|
|
5
|
+
/** One fetchable streaming transducer. */
|
|
6
|
+
export interface RecognitionModelEntry extends ModelEntry {
|
|
7
|
+
readonly description: string;
|
|
8
|
+
/** Primary language subtags this model recognizes (`de` covers `de-DE`, `de-AT`, …). */
|
|
9
|
+
readonly langs: readonly string[];
|
|
10
|
+
/** The best `quality` this model honestly serves; a request for more is `unavailable`. */
|
|
11
|
+
readonly quality: RecognitionQuality;
|
|
12
|
+
readonly sampleRate: number;
|
|
13
|
+
/** Whether `phrases` can bias it. False = error `phrases-not-supported`, as the spec says. */
|
|
14
|
+
readonly hotwords: boolean;
|
|
15
|
+
/** File names inside the entry, by role. */
|
|
16
|
+
readonly encoder: string;
|
|
17
|
+
readonly decoder: string;
|
|
18
|
+
readonly joiner: string;
|
|
19
|
+
readonly tokens: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const DEFAULT_RECOGNITION_MODEL = "zipformer-de-kroko";
|
|
22
|
+
export declare const RECOGNITION_MODELS: readonly RecognitionModelEntry[];
|
|
23
|
+
export declare function findRecognitionModel(id?: string): RecognitionModelEntry | undefined;
|
|
24
|
+
/** The first model that recognizes `lang` (a BCP 47 tag) at `quality` or better. */
|
|
25
|
+
export declare function modelFor(lang: string, quality?: RecognitionQuality): RecognitionModelEntry | undefined;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type FetchLike, type ModelAvailability } from '@gjsify/model-store';
|
|
2
|
+
import { type RecognitionModelEntry, type RecognitionQuality } from './model-manifest.js';
|
|
3
|
+
export type SpeechRecognitionAvailability = ModelAvailability;
|
|
4
|
+
export interface SpeechRecognitionOptions {
|
|
5
|
+
langs: string[];
|
|
6
|
+
/** Spec default `false`. Plays no part here: every engine in this package is local. */
|
|
7
|
+
processLocally?: boolean;
|
|
8
|
+
/** Spec default `command`. A model that cannot honestly serve it is `unavailable`. */
|
|
9
|
+
quality?: RecognitionQuality;
|
|
10
|
+
}
|
|
11
|
+
export interface SpeechRecognitionInstallOptions {
|
|
12
|
+
langs: string[];
|
|
13
|
+
quality?: RecognitionQuality;
|
|
14
|
+
/** Called at least once, with `received === total` on success. */
|
|
15
|
+
onProgress?: (progress: {
|
|
16
|
+
modelId: string;
|
|
17
|
+
received: number;
|
|
18
|
+
total: number;
|
|
19
|
+
}) => void;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
/** Override the global `fetch`. Tests only. */
|
|
22
|
+
fetchImpl?: FetchLike;
|
|
23
|
+
}
|
|
24
|
+
/** `<user cache>/gjsify/models/speech` — shared with `@gjsify/vad`. */
|
|
25
|
+
export declare function modelCacheDir(): string;
|
|
26
|
+
/** Absolute paths of a model's files, for the engine. Throws when it is not cached. */
|
|
27
|
+
export declare function resolveRecognitionModel(entry: RecognitionModelEntry): Record<string, string>;
|
|
28
|
+
/** Whether `entry` is fully cached. */
|
|
29
|
+
export declare function isModelCached(entry: RecognitionModelEntry): boolean;
|
|
30
|
+
/** Throws SyntaxError unless `tag` is a structurally valid BCP 47 language tag. */
|
|
31
|
+
export declare function validateLanguageTag(tag: string): void;
|
|
32
|
+
/** `SpeechRecognition.available()`. QUIET: never downloads, never loads a model. */
|
|
33
|
+
export declare function available(options: SpeechRecognitionOptions): Promise<SpeechRecognitionAvailability>;
|
|
34
|
+
/** `SpeechRecognition.install()`: fetch what `available()` would call `downloadable`. */
|
|
35
|
+
export declare function install(options: SpeechRecognitionInstallOptions): Promise<boolean>;
|
|
36
|
+
/** Delete the cached files for `id`. `false` when there was nothing to delete. */
|
|
37
|
+
export declare function uninstall(id?: string): boolean;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Event, EventTarget } from '@gjsify/dom-events';
|
|
2
|
+
import { type PcmFormat, type VoiceActivityDetector } from '@gjsify/vad';
|
|
3
|
+
import { type RecognizerEngine } from './backend.js';
|
|
4
|
+
import { type RecognitionModelEntry } from './model-manifest.js';
|
|
5
|
+
import { type SpeechRecognitionAvailability, type SpeechRecognitionInstallOptions, type SpeechRecognitionOptions } from './models.js';
|
|
6
|
+
import { type SpeechRecognitionPhrase } from './results.js';
|
|
7
|
+
/** As much of `@gjsify/media-capture`'s `PcmSource` as a session reads. */
|
|
8
|
+
export interface PcmReader {
|
|
9
|
+
readonly sampleRate?: number;
|
|
10
|
+
readonly format?: PcmFormat;
|
|
11
|
+
start?(): void;
|
|
12
|
+
read(): Promise<Uint8Array | Int16Array | Float32Array | null>;
|
|
13
|
+
stop(): void;
|
|
14
|
+
}
|
|
15
|
+
/** Replace the parts of a session that touch hardware or a model. Tests, and apps with their own audio. */
|
|
16
|
+
export interface SpeechRecognitionInit {
|
|
17
|
+
/** The engine for `model`. Default: sherpa-onnx on the cached files. */
|
|
18
|
+
engine?: (model: RecognitionModelEntry) => RecognizerEngine;
|
|
19
|
+
/** The detector, or `null` for none. Default: Silero when its model is cached. */
|
|
20
|
+
vad?: () => VoiceActivityDetector | null;
|
|
21
|
+
/** The audio. Default: the default microphone at 16 kHz mono through `@gjsify/media-capture`. */
|
|
22
|
+
source?: () => PcmReader;
|
|
23
|
+
}
|
|
24
|
+
export declare class SpeechRecognition extends EventTarget {
|
|
25
|
+
#private;
|
|
26
|
+
lang: string;
|
|
27
|
+
continuous: boolean;
|
|
28
|
+
interimResults: boolean;
|
|
29
|
+
maxAlternatives: number;
|
|
30
|
+
unspokenPunctuation: boolean;
|
|
31
|
+
phrases: SpeechRecognitionPhrase[];
|
|
32
|
+
constructor(init?: SpeechRecognitionInit);
|
|
33
|
+
get processLocally(): boolean;
|
|
34
|
+
set processLocally(value: boolean);
|
|
35
|
+
static available(options: SpeechRecognitionOptions): Promise<SpeechRecognitionAvailability>;
|
|
36
|
+
static install(options: SpeechRecognitionInstallOptions): Promise<boolean>;
|
|
37
|
+
start(track?: unknown): void;
|
|
38
|
+
/** Stop capturing; what was heard so far is still recognized and delivered. */
|
|
39
|
+
stop(): void;
|
|
40
|
+
/** Stop capturing and discard everything; only `end` follows. */
|
|
41
|
+
abort(): void;
|
|
42
|
+
}
|
|
43
|
+
type Handler = ((event: Event) => void) | null;
|
|
44
|
+
/** The `on<event>` IDL attributes, installed on the prototype by {@link defineEventHandler}. */
|
|
45
|
+
export interface SpeechRecognition {
|
|
46
|
+
onaudiostart: Handler;
|
|
47
|
+
onsoundstart: Handler;
|
|
48
|
+
onspeechstart: Handler;
|
|
49
|
+
onspeechend: Handler;
|
|
50
|
+
onsoundend: Handler;
|
|
51
|
+
onaudioend: Handler;
|
|
52
|
+
onresult: Handler;
|
|
53
|
+
onnomatch: Handler;
|
|
54
|
+
onerror: Handler;
|
|
55
|
+
onstart: Handler;
|
|
56
|
+
onend: Handler;
|
|
57
|
+
}
|
|
58
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare class SpeechRecognitionAlternative {
|
|
2
|
+
readonly transcript: string;
|
|
3
|
+
readonly confidence: number;
|
|
4
|
+
constructor(transcript: string, confidence?: number);
|
|
5
|
+
get [Symbol.toStringTag](): string;
|
|
6
|
+
}
|
|
7
|
+
export interface SpeechRecognitionResultInit {
|
|
8
|
+
alternatives: SpeechRecognitionAlternative[];
|
|
9
|
+
isFinal: boolean;
|
|
10
|
+
/** Seconds on the session's audio timeline, which starts at 0.0 at the first frame consumed. */
|
|
11
|
+
speechStartTime?: number;
|
|
12
|
+
speechEndTime?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class SpeechRecognitionResult {
|
|
15
|
+
readonly isFinal: boolean;
|
|
16
|
+
readonly speechStartTime: number;
|
|
17
|
+
readonly speechEndTime: number;
|
|
18
|
+
readonly length: number;
|
|
19
|
+
readonly [index: number]: SpeechRecognitionAlternative;
|
|
20
|
+
constructor(init: SpeechRecognitionResultInit);
|
|
21
|
+
item(index: number): SpeechRecognitionAlternative;
|
|
22
|
+
get [Symbol.toStringTag](): string;
|
|
23
|
+
}
|
|
24
|
+
export declare class SpeechRecognitionResultList {
|
|
25
|
+
readonly length: number;
|
|
26
|
+
readonly [index: number]: SpeechRecognitionResult;
|
|
27
|
+
constructor(results?: readonly SpeechRecognitionResult[]);
|
|
28
|
+
item(index: number): SpeechRecognitionResult;
|
|
29
|
+
[Symbol.iterator](): Iterator<SpeechRecognitionResult>;
|
|
30
|
+
get [Symbol.toStringTag](): string;
|
|
31
|
+
}
|
|
32
|
+
/** A contextual-biasing term. `boost` is advisory and must lie in [0, 10]. */
|
|
33
|
+
export declare class SpeechRecognitionPhrase {
|
|
34
|
+
readonly phrase: string;
|
|
35
|
+
readonly boost: number;
|
|
36
|
+
constructor(phrase: string, boost?: number);
|
|
37
|
+
get [Symbol.toStringTag](): string;
|
|
38
|
+
}
|
|
39
|
+
/** A `SyntaxError` DOMException where the runtime has one, a named `Error` otherwise. */
|
|
40
|
+
export declare function syntaxError(message: string): Error;
|
|
41
|
+
export declare function namedError(message: string, name: string): Error;
|
package/package.json
CHANGED
|
@@ -1,6 +1,94 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@gjsify/speech-recognition",
|
|
3
|
+
"version": "0.64.33-next.323",
|
|
4
|
+
"description": "Web Speech recognition API (SpeechRecognition and its event and result classes) for GJS, running fully on the device: a streaming zipformer on sherpa-onnx, fed by @gjsify/media-capture and endpointed by @gjsify/vad. Models are fetched on first use, never bundled.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"module": "lib/esm/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"default": "./lib/esm/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./register": {
|
|
14
|
+
"types": "./lib/types/register.d.ts",
|
|
15
|
+
"default": "./lib/esm/register.js"
|
|
16
|
+
},
|
|
17
|
+
"./globals": "./globals.mjs"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"lib",
|
|
21
|
+
"globals.mjs"
|
|
22
|
+
],
|
|
23
|
+
"sideEffects": [
|
|
24
|
+
"./lib/esm/register.js",
|
|
25
|
+
"./globals.mjs"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"clear": "gjsify clear lib tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo test.gjs.mjs",
|
|
29
|
+
"check": "gjsify tsc --noEmit",
|
|
30
|
+
"build": "gjsify run build:gjsify && gjsify run build:types",
|
|
31
|
+
"build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test*.{mts,ts}'",
|
|
32
|
+
"build:types": "gjsify tsc",
|
|
33
|
+
"build:test": "gjsify run build:test:gjs",
|
|
34
|
+
"build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
|
|
35
|
+
"test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:gjs",
|
|
36
|
+
"test:gjs": "gjsify run test.gjs.mjs"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"gjs",
|
|
40
|
+
"speech",
|
|
41
|
+
"speechrecognition",
|
|
42
|
+
"stt",
|
|
43
|
+
"asr",
|
|
44
|
+
"sherpa-onnx",
|
|
45
|
+
"zipformer"
|
|
46
|
+
],
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@gjsify/dom-events": "0.64.33-next.323",
|
|
49
|
+
"@gjsify/media-capture": "0.64.33-next.323",
|
|
50
|
+
"@gjsify/model-store": "0.64.33-next.323",
|
|
51
|
+
"@gjsify/node-globals": "0.64.33-next.323",
|
|
52
|
+
"@gjsify/sherpa-native": "0.64.33-next.323",
|
|
53
|
+
"@gjsify/utils": "0.64.33-next.323",
|
|
54
|
+
"@gjsify/vad": "0.64.33-next.323"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@girs/gjs": "5.4.0",
|
|
58
|
+
"@girs/glib-2.0": "5.4.0",
|
|
59
|
+
"@gjsify/cli": "0.64.33-next.323",
|
|
60
|
+
"@gjsify/unit": "0.64.33-next.323",
|
|
61
|
+
"@types/node": "^25.9.2",
|
|
62
|
+
"typescript": "^6.0.3"
|
|
63
|
+
},
|
|
64
|
+
"gjsify": {
|
|
65
|
+
"runtimes": {
|
|
66
|
+
"gjs": "polyfill",
|
|
67
|
+
"node": "none",
|
|
68
|
+
"browser": "native",
|
|
69
|
+
"nativescript": "none"
|
|
70
|
+
},
|
|
71
|
+
"os": {
|
|
72
|
+
"linux": "partial",
|
|
73
|
+
"darwin": "none",
|
|
74
|
+
"win32": "none"
|
|
75
|
+
},
|
|
76
|
+
"osNotes": {
|
|
77
|
+
"linux": "Needs the @gjsify/sherpa-native prebuild (linux-x64 only today) and a cached German model. Without them SpeechRecognition.available() answers 'unavailable' and start() fires error service-not-allowed.",
|
|
78
|
+
"darwin": "@gjsify/sherpa-native is not built for macOS yet, so there is no engine. available() answers 'unavailable'.",
|
|
79
|
+
"win32": "@gjsify/sherpa-native is not built for Windows yet, so there is no engine. available() answers 'unavailable'."
|
|
80
|
+
},
|
|
81
|
+
"tier": 3
|
|
82
|
+
},
|
|
83
|
+
"license": "MIT",
|
|
84
|
+
"repository": {
|
|
85
|
+
"type": "git",
|
|
86
|
+
"url": "git+https://github.com/gjsify/gjsify.git",
|
|
87
|
+
"directory": "packages/web/speech-recognition"
|
|
88
|
+
},
|
|
89
|
+
"bugs": {
|
|
90
|
+
"url": "https://github.com/gjsify/gjsify/issues"
|
|
91
|
+
},
|
|
92
|
+
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/web/speech-recognition#readme",
|
|
93
|
+
"gitHead": "603d30dc7b5600376fca2b715ecc154cc12b4b31"
|
|
94
|
+
}
|