@lumiastream/wakeword 1.1.5-alpha.2 → 1.1.5-alpha.4
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/lib/voice.js +49 -14
- package/package.json +1 -1
package/lib/voice.js
CHANGED
|
@@ -5,6 +5,14 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
import { existsSync, chmodSync } from "node:fs";
|
|
6
6
|
import readline from "node:readline";
|
|
7
7
|
|
|
8
|
+
/* ------------------------------------------------------------------ */
|
|
9
|
+
/* 0. Helpers */
|
|
10
|
+
/* ------------------------------------------------------------------ */
|
|
11
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
const ASAR_TOKEN = "app.asar";
|
|
14
|
+
const ASAR_UNPACKED_TOKEN = "app.asar.unpacked";
|
|
15
|
+
|
|
8
16
|
// Ensure native libs can load from app.asar.unpacked when packaged
|
|
9
17
|
const maybeUnpackedPath = (libPath) => {
|
|
10
18
|
if (typeof libPath !== "string") return libPath;
|
|
@@ -33,14 +41,6 @@ koffi.load = (libPath, ...rest) => {
|
|
|
33
41
|
|
|
34
42
|
const { Model, Recognizer, setLogLevel } = await import("vosk-koffi");
|
|
35
43
|
|
|
36
|
-
/* ------------------------------------------------------------------ */
|
|
37
|
-
/* 0. Helpers */
|
|
38
|
-
/* ------------------------------------------------------------------ */
|
|
39
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
40
|
-
|
|
41
|
-
const ASAR_TOKEN = "app.asar";
|
|
42
|
-
const ASAR_UNPACKED_TOKEN = "app.asar.unpacked";
|
|
43
|
-
|
|
44
44
|
function unpacked(p) {
|
|
45
45
|
if (!p || !p.includes(ASAR_TOKEN)) return p;
|
|
46
46
|
if (p.includes(ASAR_UNPACKED_TOKEN)) return p;
|
|
@@ -156,6 +156,9 @@ const LOG_PARTIAL =
|
|
|
156
156
|
["1", "true", "yes"].includes(
|
|
157
157
|
(process.env.WAKEWORD_LOG_PARTIAL || "").toLowerCase()
|
|
158
158
|
);
|
|
159
|
+
let LOG_FINAL = ["1", "true", "yes"].includes(
|
|
160
|
+
(process.env.WAKEWORD_LOG_FINAL || "").toLowerCase()
|
|
161
|
+
);
|
|
159
162
|
let lastLevelLog = 0;
|
|
160
163
|
|
|
161
164
|
function logAudioLevel(buf) {
|
|
@@ -222,25 +225,49 @@ mic.on("data", (buf) => {
|
|
|
222
225
|
function handle(processedWord, averageConfidence, originalText) {
|
|
223
226
|
if (!processedWord && !originalText) return;
|
|
224
227
|
|
|
228
|
+
const finalSentence =
|
|
229
|
+
typeof originalText === "string" && originalText.trim()
|
|
230
|
+
? originalText.trim()
|
|
231
|
+
: (processedWord ?? "").toString().trim();
|
|
232
|
+
if (LOG_FINAL && finalSentence) {
|
|
233
|
+
process.stdout?.write(`final|${finalSentence}\n`);
|
|
234
|
+
}
|
|
235
|
+
|
|
225
236
|
const normalizedProcessed = normalizePhrase(processedWord);
|
|
226
237
|
const normalizedOriginal = normalizePhrase(originalText);
|
|
227
238
|
const matches = new Set();
|
|
239
|
+
const confidentCommands = new Set();
|
|
228
240
|
|
|
229
|
-
const findMatches = (text) => {
|
|
241
|
+
const findMatches = (text, allowedCommands = COMMANDS) => {
|
|
230
242
|
if (!text || text.includes(UNKNOWN_TOKEN)) return;
|
|
231
243
|
const hits = MATCH_SENTENCE
|
|
232
|
-
?
|
|
233
|
-
:
|
|
244
|
+
? allowedCommands.filter((command) => text.includes(command))
|
|
245
|
+
: allowedCommands.filter((command) => text === command);
|
|
234
246
|
hits.forEach((hit) => matches.add(hit));
|
|
235
247
|
};
|
|
236
248
|
|
|
237
|
-
//
|
|
249
|
+
// Only allow sentence matches for commands that were confidently recognized.
|
|
250
|
+
if (normalizedProcessed) {
|
|
251
|
+
COMMANDS.forEach((command) => {
|
|
252
|
+
const isMatch = MATCH_SENTENCE
|
|
253
|
+
? normalizedProcessed.includes(command)
|
|
254
|
+
: normalizedProcessed === command;
|
|
255
|
+
if (isMatch) {
|
|
256
|
+
confidentCommands.add(command);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Try the filtered text first, then fall back to the raw sentence only for confident commands.
|
|
238
262
|
findMatches(normalizedProcessed);
|
|
239
|
-
findMatches(normalizedOriginal);
|
|
263
|
+
findMatches(normalizedOriginal, [...confidentCommands]);
|
|
240
264
|
|
|
241
265
|
if (!matches.size) return;
|
|
242
266
|
|
|
243
267
|
matches.forEach((match) => {
|
|
268
|
+
if (finalSentence) {
|
|
269
|
+
process.stdout?.write(`sentence|${finalSentence}\n`);
|
|
270
|
+
}
|
|
244
271
|
process.stdout?.write(`voice|${match}\n`);
|
|
245
272
|
process.stdout?.write(`confidence|${averageConfidence}\n`);
|
|
246
273
|
});
|
|
@@ -252,13 +279,21 @@ const rl = readline.createInterface({ input: process.stdin, terminal: false });
|
|
|
252
279
|
|
|
253
280
|
rl.on("line", (line) => {
|
|
254
281
|
const trimmed = line.trim();
|
|
255
|
-
if (
|
|
282
|
+
if (
|
|
283
|
+
!trimmed.startsWith("update,") &&
|
|
284
|
+
!trimmed.startsWith("confidence,") &&
|
|
285
|
+
!trimmed.startsWith("debug,")
|
|
286
|
+
)
|
|
256
287
|
return;
|
|
257
288
|
|
|
258
289
|
if (trimmed.startsWith("confidence,")) {
|
|
259
290
|
WORD_CONFIDENCE_THRESHOLD = Number(trimmed.split(",")[1]);
|
|
260
291
|
return;
|
|
261
292
|
}
|
|
293
|
+
if (trimmed.startsWith("debug,")) {
|
|
294
|
+
LOG_FINAL = toBool(trimmed.split(",")[1]);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
262
297
|
|
|
263
298
|
const phrases = trimmed
|
|
264
299
|
.split(",")
|