@lumiastream/wakeword 1.0.1-alpha.5 → 1.0.1-alpha.7
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/bin/wakeword +3 -4
- package/binaries/sox.exe +0 -0
- package/binaries/soxlinux +0 -0
- package/binaries/soxmac +0 -0
- package/lib/record.js +119 -0
- package/lib/recorders/arecord.js +27 -0
- package/lib/recorders/index.js +18 -0
- package/lib/recorders/rec.js +36 -0
- package/lib/recorders/sox.js +51 -0
- package/lib/voice.mjs +1 -1
- package/package.json +4 -9
package/bin/wakeword
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
2
|
import { fileURLToPath } from "url";
|
|
4
3
|
import { spawn } from "node:child_process";
|
|
5
4
|
import path from "node:path";
|
|
6
5
|
|
|
7
6
|
// Pick correct SoX binary for the current OS
|
|
8
7
|
const exe = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
win32: "sox.exe",
|
|
9
|
+
darwin: "soxmac",
|
|
10
|
+
linux: "soxlinux",
|
|
12
11
|
}[process.platform];
|
|
13
12
|
|
|
14
13
|
const soxPath = path.join(
|
package/binaries/sox.exe
ADDED
|
Binary file
|
|
Binary file
|
package/binaries/soxmac
ADDED
|
Binary file
|
package/lib/record.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const assert = require("assert");
|
|
4
|
+
const debug = require("debug")("record");
|
|
5
|
+
const { spawn } = require("child_process");
|
|
6
|
+
const recorders = require("./recorders");
|
|
7
|
+
|
|
8
|
+
class Recording {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
const defaults = {
|
|
11
|
+
sampleRate: 16000,
|
|
12
|
+
channels: 1,
|
|
13
|
+
compress: false,
|
|
14
|
+
threshold: 0.5,
|
|
15
|
+
thresholdStart: null,
|
|
16
|
+
thresholdEnd: null,
|
|
17
|
+
silence: "1.0",
|
|
18
|
+
recorder: "sox",
|
|
19
|
+
endOnSilence: false,
|
|
20
|
+
audioType: "wav",
|
|
21
|
+
binPath: null,
|
|
22
|
+
bufferSize: null,
|
|
23
|
+
arguments: [],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
this.options = Object.assign(defaults, options);
|
|
27
|
+
|
|
28
|
+
const recorder = recorders.load(this.options.recorder);
|
|
29
|
+
const { cmd, args, spawnOptions = {} } = recorder(this.options);
|
|
30
|
+
|
|
31
|
+
this.cmd = cmd;
|
|
32
|
+
this.args = args;
|
|
33
|
+
this.cmdOptions = Object.assign(
|
|
34
|
+
{ encoding: "binary", stdio: "pipe" },
|
|
35
|
+
spawnOptions
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
debug(`Started recording`);
|
|
39
|
+
debug(this.options);
|
|
40
|
+
|
|
41
|
+
const command = ` ${this.cmd} ${this.args.join(" ")}`;
|
|
42
|
+
debug(command);
|
|
43
|
+
|
|
44
|
+
return this.start();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
start() {
|
|
48
|
+
const { cmd, args, cmdOptions } = this;
|
|
49
|
+
|
|
50
|
+
const cp = spawn(cmd, args, cmdOptions);
|
|
51
|
+
const rec = cp.stdout;
|
|
52
|
+
const err = cp.stderr;
|
|
53
|
+
|
|
54
|
+
this.process = cp; // expose child process
|
|
55
|
+
this._stream = rec; // expose output stream
|
|
56
|
+
|
|
57
|
+
cp.on("close", (code) => {
|
|
58
|
+
if (code === 0) return;
|
|
59
|
+
rec.emit(
|
|
60
|
+
"error",
|
|
61
|
+
`${this.cmd} has exited with error code ${code}.
|
|
62
|
+
|
|
63
|
+
Enable debugging with the environment variable DEBUG=record.`
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
err.on("data", (chunk) => {
|
|
68
|
+
debug(`STDERR: ${chunk}`);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
rec.on("data", (chunk) => {
|
|
72
|
+
debug(`Recording ${chunk.length} bytes`);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
rec.on("end", () => {
|
|
76
|
+
debug("Recording ended");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
stop() {
|
|
83
|
+
assert(this.process, "Recording not yet started");
|
|
84
|
+
|
|
85
|
+
this.process.kill();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
pause() {
|
|
89
|
+
assert(this.process, "Recording not yet started");
|
|
90
|
+
|
|
91
|
+
this.process.kill("SIGSTOP");
|
|
92
|
+
this._stream.pause();
|
|
93
|
+
debug("Paused recording");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
resume() {
|
|
97
|
+
assert(this.process, "Recording not yet started");
|
|
98
|
+
|
|
99
|
+
this.process.kill("SIGCONT");
|
|
100
|
+
this._stream.resume();
|
|
101
|
+
debug("Resumed recording");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
isPaused() {
|
|
105
|
+
assert(this.process, "Recording not yet started");
|
|
106
|
+
|
|
107
|
+
return this._stream.isPaused();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
stream() {
|
|
111
|
+
assert(this._stream, "Recording not yet started");
|
|
112
|
+
|
|
113
|
+
return this._stream;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
record: (...args) => new Recording(...args),
|
|
119
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// On some systems (RasPi), arecord is the prefered recording binary
|
|
2
|
+
module.exports = (options) => {
|
|
3
|
+
let cmd = "arecord";
|
|
4
|
+
|
|
5
|
+
if (options.binPath) {
|
|
6
|
+
cmd = options.binPath;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const args = [
|
|
10
|
+
"-q", // show no progress
|
|
11
|
+
"-r",
|
|
12
|
+
options.sampleRate, // sample rate
|
|
13
|
+
"-c",
|
|
14
|
+
options.channels, // channels
|
|
15
|
+
"-t",
|
|
16
|
+
options.audioType, // audio type
|
|
17
|
+
"-f",
|
|
18
|
+
"S16_LE", // Sample format
|
|
19
|
+
"-", // pipe
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
if (options.device) {
|
|
23
|
+
args.unshift("-D", options.device);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return { cmd, args };
|
|
27
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
|
|
3
|
+
function load (recorderName) {
|
|
4
|
+
try {
|
|
5
|
+
const recoderPath = path.resolve(__dirname, recorderName)
|
|
6
|
+
return require(recoderPath)
|
|
7
|
+
} catch (err) {
|
|
8
|
+
if (err.code === 'MODULE_NOT_FOUND') {
|
|
9
|
+
throw new Error(`No such recorder found: ${recorderName}`)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
throw err
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
load
|
|
18
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module.exports = (options) => {
|
|
2
|
+
let cmd = "rec";
|
|
3
|
+
|
|
4
|
+
if (options.binPath) {
|
|
5
|
+
cmd = options.binPath;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let args = [
|
|
9
|
+
"-q", // show no progress
|
|
10
|
+
"-r",
|
|
11
|
+
options.sampleRate, // sample rate
|
|
12
|
+
"-c",
|
|
13
|
+
options.channels, // channels
|
|
14
|
+
"-e",
|
|
15
|
+
"signed-integer", // sample encoding
|
|
16
|
+
"-b",
|
|
17
|
+
"16", // precision (bits)
|
|
18
|
+
"-t",
|
|
19
|
+
options.audioType, // audio type
|
|
20
|
+
"-", // pipe
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
if (options.endOnSilence) {
|
|
24
|
+
args = args.concat([
|
|
25
|
+
"silence",
|
|
26
|
+
"1",
|
|
27
|
+
"0.1",
|
|
28
|
+
options.thresholdStart || options.threshold + "%",
|
|
29
|
+
"1",
|
|
30
|
+
options.silence,
|
|
31
|
+
options.thresholdEnd || options.threshold + "%",
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { cmd, args };
|
|
36
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module.exports = (options) => {
|
|
2
|
+
let cmd = "sox";
|
|
3
|
+
|
|
4
|
+
if (options.binPath) {
|
|
5
|
+
cmd = options.binPath;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let args = [
|
|
9
|
+
"--default-device",
|
|
10
|
+
"--no-show-progress", // show no progress
|
|
11
|
+
"--rate",
|
|
12
|
+
options.sampleRate, // sample rate
|
|
13
|
+
"--channels",
|
|
14
|
+
options.channels, // channels
|
|
15
|
+
"--encoding",
|
|
16
|
+
"signed-integer", // sample encoding
|
|
17
|
+
"--bits",
|
|
18
|
+
"16", // precision (bits)
|
|
19
|
+
"--type",
|
|
20
|
+
options.audioType, // audio type
|
|
21
|
+
"-", // pipe
|
|
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
|
+
if (options.device) {
|
|
47
|
+
spawnOptions.env = { ...process.env, AUDIODEV: options.device };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { cmd, args, spawnOptions };
|
|
51
|
+
};
|
package/lib/voice.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumiastream/wakeword",
|
|
3
|
-
"version": "1.0.1-alpha.
|
|
3
|
+
"version": "1.0.1-alpha.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "lib/voice.mjs",
|
|
6
6
|
"bin": {
|
|
@@ -9,18 +9,13 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
11
11
|
"lib/",
|
|
12
|
-
"models/"
|
|
12
|
+
"models/",
|
|
13
|
+
"binaries/"
|
|
13
14
|
],
|
|
14
15
|
"scripts": {
|
|
15
|
-
"postinstall": "chmod +x binaries/soxmac
|
|
16
|
-
},
|
|
17
|
-
"bundledDependencies": {
|
|
18
|
-
"@lumiastream/wakeword-darwin": "file:binaries/soxmac",
|
|
19
|
-
"@lumiastream/wakeword-linux": "file:binaries/soxlinux",
|
|
20
|
-
"@lumiastream/wakeword-win32": "file:binaries/soxwindows"
|
|
16
|
+
"postinstall": "chmod +x binaries/soxmac binaries/soxlinux binaries/sox.exe || true"
|
|
21
17
|
},
|
|
22
18
|
"dependencies": {
|
|
23
|
-
"@lumiastream/record": "^1.0.1",
|
|
24
19
|
"vosk": "^0.3.39"
|
|
25
20
|
}
|
|
26
21
|
}
|