@lumiastream/wakeword 1.1.0 → 1.1.2
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/list-devices.js +41 -49
- package/lib/recorders/sox.js +69 -51
- package/lib/test-devices.js +3 -1
- package/lib/voice.js +13 -2
- package/package.json +1 -1
package/lib/list-devices.js
CHANGED
|
@@ -31,8 +31,9 @@ export function listAudioDevices() {
|
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// Windows:
|
|
35
|
-
const
|
|
34
|
+
// Windows: Use PowerShell to get audio devices
|
|
35
|
+
const psCommand = `Get-WmiObject Win32_SoundDevice | Select-Object -Property Name, DeviceID | ConvertTo-Json`;
|
|
36
|
+
const proc = spawn("powershell", ["-Command", psCommand], {
|
|
36
37
|
encoding: "utf8",
|
|
37
38
|
windowsHide: true,
|
|
38
39
|
});
|
|
@@ -51,69 +52,60 @@ export function listAudioDevices() {
|
|
|
51
52
|
proc.on("close", (code) => {
|
|
52
53
|
const devices = [];
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Parse device entries (typically in format "0: Device Name")
|
|
71
|
-
if (inDeviceList) {
|
|
72
|
-
const match = line.match(/^\s*(\d+):\s*(.+)$/);
|
|
73
|
-
if (match) {
|
|
74
|
-
devices.push({
|
|
75
|
-
id: match[1],
|
|
76
|
-
name: match[2].trim(),
|
|
77
|
-
});
|
|
78
|
-
}
|
|
55
|
+
try {
|
|
56
|
+
// Try to parse PowerShell JSON output
|
|
57
|
+
if (output.trim()) {
|
|
58
|
+
const psDevices = JSON.parse(output);
|
|
59
|
+
const deviceArray = Array.isArray(psDevices) ? psDevices : [psDevices];
|
|
60
|
+
|
|
61
|
+
deviceArray.forEach((device, index) => {
|
|
62
|
+
if (device && device.Name) {
|
|
63
|
+
devices.push({
|
|
64
|
+
id: index.toString(),
|
|
65
|
+
name: device.Name,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
79
69
|
}
|
|
70
|
+
} catch (e) {
|
|
71
|
+
// PowerShell failed, try alternative method
|
|
80
72
|
}
|
|
81
73
|
|
|
74
|
+
// If PowerShell didn't work, try using Get-AudioDevice module if available
|
|
82
75
|
if (devices.length === 0) {
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
for (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
id: match[1],
|
|
91
|
-
name: name,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
76
|
+
// Provide numbered device options for manual selection
|
|
77
|
+
// SoX on Windows typically uses device numbers 0-9
|
|
78
|
+
for (let i = 0; i <= 5; i++) {
|
|
79
|
+
devices.push({
|
|
80
|
+
id: i.toString(),
|
|
81
|
+
name: `Audio Input Device ${i}${i === 0 ? ' (Default)' : ''}`,
|
|
82
|
+
});
|
|
94
83
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
devices.push(
|
|
100
|
-
{ id: "0", name: "Device 0 (Default)" },
|
|
101
|
-
{ id: "1", name: "Device 1" },
|
|
102
|
-
{ id: "2", name: "Device 2" }
|
|
103
|
-
);
|
|
84
|
+
|
|
85
|
+
console.log("\nNote: Unable to query actual device names.");
|
|
86
|
+
console.log("Showing generic device numbers (0-5).");
|
|
87
|
+
console.log("You may need to test each to find your microphone.\n");
|
|
104
88
|
}
|
|
105
89
|
|
|
106
90
|
resolve(devices);
|
|
107
91
|
});
|
|
108
92
|
|
|
109
93
|
proc.on("error", (err) => {
|
|
110
|
-
|
|
94
|
+
// If PowerShell is not available, provide default options
|
|
95
|
+
const devices = [];
|
|
96
|
+
for (let i = 0; i <= 5; i++) {
|
|
97
|
+
devices.push({
|
|
98
|
+
id: i.toString(),
|
|
99
|
+
name: `Audio Input Device ${i}${i === 0 ? ' (Default)' : ''}`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
resolve(devices);
|
|
111
103
|
});
|
|
112
104
|
});
|
|
113
105
|
}
|
|
114
106
|
|
|
115
107
|
// If run directly, list devices
|
|
116
|
-
if (import.meta.url
|
|
108
|
+
if (process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, '/'))) {
|
|
117
109
|
listAudioDevices()
|
|
118
110
|
.then((devices) => {
|
|
119
111
|
console.log("Available audio input devices:");
|
package/lib/recorders/sox.js
CHANGED
|
@@ -1,53 +1,71 @@
|
|
|
1
1
|
export default (options) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
2
|
+
let cmd = "sox";
|
|
3
|
+
|
|
4
|
+
if (options.binPath) {
|
|
5
|
+
cmd = options.binPath;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Build common output/encoding args
|
|
9
|
+
let args = [
|
|
10
|
+
"--no-show-progress",
|
|
11
|
+
"--rate",
|
|
12
|
+
options.sampleRate,
|
|
13
|
+
"--channels",
|
|
14
|
+
options.channels,
|
|
15
|
+
"--encoding",
|
|
16
|
+
"signed-integer",
|
|
17
|
+
"--bits",
|
|
18
|
+
"16",
|
|
19
|
+
"--type",
|
|
20
|
+
options.audioType,
|
|
21
|
+
"-", // write to stdout
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
if (options.bufferSize) {
|
|
25
|
+
args.push("--buffer", options.bufferSize);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (options.endOnSilence) {
|
|
29
|
+
args = args.concat([
|
|
30
|
+
"silence",
|
|
31
|
+
"1",
|
|
32
|
+
"0.1",
|
|
33
|
+
options.thresholdStart || options.threshold + "%",
|
|
34
|
+
"1",
|
|
35
|
+
options.silence,
|
|
36
|
+
options.thresholdEnd || options.threshold + "%",
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (options.arguments) {
|
|
41
|
+
args = args.concat(options.arguments);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const spawnOptions = {};
|
|
45
|
+
|
|
46
|
+
// Prepend input spec based on platform
|
|
47
|
+
const platform = process.platform;
|
|
48
|
+
if (platform === "win32") {
|
|
49
|
+
const dev = options.device ?? "default";
|
|
50
|
+
args.unshift("-t", "waveaudio", dev);
|
|
51
|
+
// AUDIODEV sometimes respected on Windows; keep for compatibility
|
|
52
|
+
spawnOptions.env = { ...process.env, AUDIODEV: dev };
|
|
53
|
+
} else if (platform === "darwin") {
|
|
54
|
+
// CoreAudio input
|
|
55
|
+
if (options.device) {
|
|
56
|
+
args.unshift("-t", "coreaudio", options.device);
|
|
57
|
+
} else {
|
|
58
|
+
// Explicitly select CoreAudio default device for reliability
|
|
59
|
+
args.unshift("-t", "coreaudio", "default");
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
// Linux: ALSA default or specified
|
|
63
|
+
if (options.device) {
|
|
64
|
+
args.unshift("-t", "alsa", options.device);
|
|
65
|
+
} else {
|
|
66
|
+
args.unshift("-d");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { cmd, args, spawnOptions };
|
|
53
71
|
};
|
package/lib/test-devices.js
CHANGED
|
@@ -13,7 +13,9 @@ async function testDevice(deviceId) {
|
|
|
13
13
|
console.log("Speak into your microphone. Press Ctrl+C to stop.\n");
|
|
14
14
|
|
|
15
15
|
const voiceScript = join(here, "voice.js");
|
|
16
|
-
|
|
16
|
+
// Pass empty string for soxPath (will use default), then deviceId
|
|
17
|
+
const args = deviceId ? [voiceScript, "", deviceId] : [voiceScript];
|
|
18
|
+
const proc = spawn("node", args, {
|
|
17
19
|
stdio: ["pipe", "inherit", "inherit"],
|
|
18
20
|
});
|
|
19
21
|
|
package/lib/voice.js
CHANGED
|
@@ -25,7 +25,10 @@ const exeName = { win32: "sox.exe", darwin: "soxmac", linux: "soxlinux" }[
|
|
|
25
25
|
|
|
26
26
|
/* Priority for sox path: argv[2] → fallback to sibling binaries/<exe> */
|
|
27
27
|
/* Priority for device: argv[3] → env var → default */
|
|
28
|
-
let soxPath = process.argv[2]
|
|
28
|
+
let soxPath = process.argv[2];
|
|
29
|
+
if (!soxPath || soxPath === "") {
|
|
30
|
+
soxPath = join(here, "..", "binaries", exeName);
|
|
31
|
+
}
|
|
29
32
|
soxPath = unpacked(soxPath);
|
|
30
33
|
|
|
31
34
|
// Parse device from argv[3] or environment variable
|
|
@@ -73,10 +76,18 @@ if (audioDevice !== null) {
|
|
|
73
76
|
// Windows: default to device 0 if not specified
|
|
74
77
|
recArgs.device = "0";
|
|
75
78
|
console.error("Using default Windows audio device: 0");
|
|
76
|
-
console.error(
|
|
79
|
+
console.error(
|
|
80
|
+
"To specify a different device, use: AUDIO_DEVICE=<device_id> or pass as 3rd argument"
|
|
81
|
+
);
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
const mic = record.record(recArgs).stream();
|
|
85
|
+
// Handle recorder (SoX) errors to avoid unhandled 'error' events
|
|
86
|
+
mic.on("error", (err) => {
|
|
87
|
+
const msg = typeof err === "string" ? err : err?.message || String(err);
|
|
88
|
+
console.error(`[wakeword] audio stream error: ${msg}`);
|
|
89
|
+
process.exit(2);
|
|
90
|
+
});
|
|
80
91
|
// Define a confidence threshold for individual words.
|
|
81
92
|
// You might need to adjust this value based on your specific use case.
|
|
82
93
|
let WORD_CONFIDENCE_THRESHOLD = 0.7;
|