@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.
@@ -31,8 +31,9 @@ export function listAudioDevices() {
31
31
  return;
32
32
  }
33
33
 
34
- // Windows: Parse available devices from SoX help output
35
- const proc = spawn(soxPath, ["-t", "waveaudio", "-h"], {
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
- // Parse output for device list
55
- const lines = (output + errorOutput).split("\n");
56
- let inDeviceList = false;
57
-
58
- for (const line of lines) {
59
- // Look for input device section
60
- if (line.includes("Input:") || line.includes("input")) {
61
- inDeviceList = true;
62
- continue;
63
- }
64
-
65
- // Stop at output section
66
- if (line.includes("Output:") || line.includes("output")) {
67
- inDeviceList = false;
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
- // Fallback: try to extract any numbered devices
84
- const allMatches = (output + errorOutput).matchAll(/(\d+):\s*([^\n]+)/g);
85
- for (const match of allMatches) {
86
- const name = match[2].trim();
87
- // Filter out obvious non-device entries
88
- if (!name.includes("SoX") && !name.includes("Usage") && name.length > 0) {
89
- devices.push({
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
- // If still no devices found, provide default options
98
- if (devices.length === 0) {
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
- reject(err);
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 === `file://${process.argv[1]}`) {
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:");
@@ -1,53 +1,71 @@
1
1
  export default (options) => {
2
- let cmd = "sox";
3
-
4
- if (options.binPath) {
5
- cmd = options.binPath;
6
- }
7
-
8
- let args = [
9
- "--no-show-progress", // show no progress
10
- "--rate",
11
- options.sampleRate, // sample rate
12
- "--channels",
13
- options.channels, // channels
14
- "--encoding",
15
- "signed-integer", // sample encoding
16
- "--bits",
17
- "16", // precision (bits)
18
- "--type",
19
- options.audioType, // audio type
20
- "-", // pipe
21
- ];
22
-
23
- if (options.bufferSize) {
24
- args.push("--buffer", options.bufferSize);
25
- }
26
-
27
- if (options.endOnSilence) {
28
- args = args.concat([
29
- "silence",
30
- "1",
31
- "0.1",
32
- options.thresholdStart || options.threshold + "%",
33
- "1",
34
- options.silence,
35
- options.thresholdEnd || options.threshold + "%",
36
- ]);
37
- }
38
-
39
- if (options.arguments) {
40
- args = args.concat(options.arguments);
41
- }
42
-
43
- const spawnOptions = {};
44
-
45
- if (options.device) {
46
- args.unshift("-t", "waveaudio", options.device);
47
- spawnOptions.env = { ...process.env, AUDIODEV: options.device };
48
- } else {
49
- args.unshift("--default-device");
50
- }
51
-
52
- return { cmd, args, spawnOptions };
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
  };
@@ -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
- const proc = spawn("node", [voiceScript, undefined, deviceId], {
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] || join(here, "..", "binaries", exeName);
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("To specify a different device, use: AUDIO_DEVICE=<device_id> or pass as 3rd argument");
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumiastream/wakeword",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "type": "module",
5
5
  "main": "lib/index.js",
6
6
  "files": [