speechflow 1.6.4 → 1.6.6
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/CHANGELOG.md +22 -0
- package/README.md +28 -3
- package/etc/speechflow.yaml +15 -13
- package/etc/stx.conf +5 -0
- package/package.json +5 -5
- package/speechflow-cli/dst/speechflow-main-api.js +3 -7
- package/speechflow-cli/dst/speechflow-main-api.js.map +1 -1
- package/speechflow-cli/dst/speechflow-main-graph.js +1 -1
- package/speechflow-cli/dst/speechflow-main.js +6 -0
- package/speechflow-cli/dst/speechflow-main.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js +1 -21
- package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.js +12 -11
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js +1 -21
- package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-expander.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-expander.js +12 -11
- package/speechflow-cli/dst/speechflow-node-a2a-expander.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js +4 -10
- package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-filler.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-filler.js +18 -16
- package/speechflow-cli/dst/speechflow-node-a2a-filler.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gain.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gain.js +8 -8
- package/speechflow-cli/dst/speechflow-node-a2a-gain.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gender.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gender.js +70 -60
- package/speechflow-cli/dst/speechflow-node-a2a-gender.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-meter.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-meter.js +58 -42
- package/speechflow-cli/dst/speechflow-node-a2a-meter.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-mute.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-mute.js +44 -10
- package/speechflow-cli/dst/speechflow-node-a2a-mute.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-pitch.d.ts +13 -0
- package/speechflow-cli/dst/speechflow-node-a2a-pitch.js +213 -0
- package/speechflow-cli/dst/speechflow-node-a2a-pitch.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-pitch2-wt.js +149 -0
- package/speechflow-cli/dst/speechflow-node-a2a-pitch2-wt.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-pitch2.d.ts +13 -0
- package/speechflow-cli/dst/speechflow-node-a2a-pitch2.js +202 -0
- package/speechflow-cli/dst/speechflow-node-a2a-pitch2.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js +13 -11
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-speex.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-speex.js +13 -12
- package/speechflow-cli/dst/speechflow-node-a2a-speex.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-vad.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-vad.js +26 -25
- package/speechflow-cli/dst/speechflow-node-a2a-vad.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-wav.js +35 -7
- package/speechflow-cli/dst/speechflow-node-a2a-wav.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-amazon.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-amazon.js +16 -16
- package/speechflow-cli/dst/speechflow-node-a2t-amazon.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js +16 -16
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-openai.d.ts +1 -2
- package/speechflow-cli/dst/speechflow-node-a2t-openai.js +15 -21
- package/speechflow-cli/dst/speechflow-node-a2t-openai.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.d.ts +1 -2
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.js +9 -15
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.d.ts +1 -2
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js +13 -18
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.d.ts +0 -1
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js +4 -10
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-amazon.js +3 -3
- package/speechflow-cli/dst/speechflow-node-t2t-amazon.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-deepl.js +2 -2
- package/speechflow-cli/dst/speechflow-node-t2t-deepl.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-format.js +36 -2
- package/speechflow-cli/dst/speechflow-node-t2t-format.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-google.js +2 -2
- package/speechflow-cli/dst/speechflow-node-t2t-google.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-modify.js +5 -5
- package/speechflow-cli/dst/speechflow-node-t2t-modify.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-ollama.js +3 -3
- package/speechflow-cli/dst/speechflow-node-t2t-ollama.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-openai.js +2 -2
- package/speechflow-cli/dst/speechflow-node-t2t-openai.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-sentence.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-sentence.js +13 -13
- package/speechflow-cli/dst/speechflow-node-t2t-sentence.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js +8 -8
- package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-transformers.js +2 -2
- package/speechflow-cli/dst/speechflow-node-t2t-transformers.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-x2x-filter.js +2 -2
- package/speechflow-cli/dst/speechflow-node-x2x-filter.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-x2x-trace.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-x2x-trace.js +42 -8
- package/speechflow-cli/dst/speechflow-node-x2x-trace.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-device.js +6 -4
- package/speechflow-cli/dst/speechflow-node-xio-device.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-file.js +19 -18
- package/speechflow-cli/dst/speechflow-node-xio-file.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-mqtt.js +13 -13
- package/speechflow-cli/dst/speechflow-node-xio-mqtt.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-websocket.js +8 -8
- package/speechflow-cli/dst/speechflow-node-xio-websocket.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node.js +6 -6
- package/speechflow-cli/dst/speechflow-node.js.map +1 -1
- package/speechflow-cli/dst/speechflow-util-audio.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-util-audio.js +22 -1
- package/speechflow-cli/dst/speechflow-util-audio.js.map +1 -1
- package/speechflow-cli/dst/speechflow-util-error.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-util-error.js +7 -1
- package/speechflow-cli/dst/speechflow-util-error.js.map +1 -1
- package/speechflow-cli/dst/speechflow-util-stream.d.ts +2 -1
- package/speechflow-cli/dst/speechflow-util-stream.js +23 -3
- package/speechflow-cli/dst/speechflow-util-stream.js.map +1 -1
- package/speechflow-cli/etc/oxlint.jsonc +2 -1
- package/speechflow-cli/etc/tsconfig.json +1 -0
- package/speechflow-cli/package.json +20 -20
- package/speechflow-cli/src/speechflow-main-api.ts +6 -13
- package/speechflow-cli/src/speechflow-main-graph.ts +1 -1
- package/speechflow-cli/src/speechflow-main.ts +4 -0
- package/speechflow-cli/src/speechflow-node-a2a-compressor-wt.ts +1 -29
- package/speechflow-cli/src/speechflow-node-a2a-compressor.ts +13 -12
- package/speechflow-cli/src/speechflow-node-a2a-expander-wt.ts +1 -29
- package/speechflow-cli/src/speechflow-node-a2a-expander.ts +13 -12
- package/speechflow-cli/src/speechflow-node-a2a-ffmpeg.ts +4 -10
- package/speechflow-cli/src/speechflow-node-a2a-filler.ts +19 -17
- package/speechflow-cli/src/speechflow-node-a2a-gain.ts +8 -8
- package/speechflow-cli/src/speechflow-node-a2a-gender.ts +83 -72
- package/speechflow-cli/src/speechflow-node-a2a-meter.ts +66 -46
- package/speechflow-cli/src/speechflow-node-a2a-mute.ts +11 -10
- package/speechflow-cli/src/speechflow-node-a2a-pitch.ts +221 -0
- package/speechflow-cli/src/speechflow-node-a2a-rnnoise.ts +14 -12
- package/speechflow-cli/src/speechflow-node-a2a-speex.ts +14 -13
- package/speechflow-cli/src/speechflow-node-a2a-vad.ts +26 -25
- package/speechflow-cli/src/speechflow-node-a2a-wav.ts +2 -7
- package/speechflow-cli/src/speechflow-node-a2t-amazon.ts +16 -16
- package/speechflow-cli/src/speechflow-node-a2t-deepgram.ts +16 -16
- package/speechflow-cli/src/speechflow-node-a2t-openai.ts +15 -21
- package/speechflow-cli/src/speechflow-node-t2a-amazon.ts +9 -15
- package/speechflow-cli/src/speechflow-node-t2a-elevenlabs.ts +13 -18
- package/speechflow-cli/src/speechflow-node-t2a-kokoro.ts +4 -10
- package/speechflow-cli/src/speechflow-node-t2t-amazon.ts +3 -3
- package/speechflow-cli/src/speechflow-node-t2t-deepl.ts +2 -2
- package/speechflow-cli/src/speechflow-node-t2t-format.ts +3 -2
- package/speechflow-cli/src/speechflow-node-t2t-google.ts +2 -2
- package/speechflow-cli/src/speechflow-node-t2t-modify.ts +6 -6
- package/speechflow-cli/src/speechflow-node-t2t-ollama.ts +3 -3
- package/speechflow-cli/src/speechflow-node-t2t-openai.ts +2 -2
- package/speechflow-cli/src/speechflow-node-t2t-sentence.ts +13 -13
- package/speechflow-cli/src/speechflow-node-t2t-subtitle.ts +12 -16
- package/speechflow-cli/src/speechflow-node-t2t-transformers.ts +2 -2
- package/speechflow-cli/src/speechflow-node-x2x-filter.ts +2 -2
- package/speechflow-cli/src/speechflow-node-x2x-trace.ts +10 -9
- package/speechflow-cli/src/speechflow-node-xio-device.ts +7 -5
- package/speechflow-cli/src/speechflow-node-xio-file.ts +20 -19
- package/speechflow-cli/src/speechflow-node-xio-mqtt.ts +14 -14
- package/speechflow-cli/src/speechflow-node-xio-websocket.ts +11 -11
- package/speechflow-cli/src/speechflow-node.ts +6 -6
- package/speechflow-cli/src/speechflow-util-audio.ts +31 -1
- package/speechflow-cli/src/speechflow-util-error.ts +9 -3
- package/speechflow-cli/src/speechflow-util-stream.ts +31 -6
- package/speechflow-ui-db/dst/index.js +25 -25
- package/speechflow-ui-db/package.json +11 -11
- package/speechflow-ui-db/src/app.vue +14 -5
- package/speechflow-ui-st/dst/index.js +460 -25
- package/speechflow-ui-st/package.json +13 -13
- package/speechflow-ui-st/src/app.vue +8 -3
- package/speechflow-cli/dst/speechflow-util-webaudio-wt.js +0 -124
- package/speechflow-cli/dst/speechflow-util-webaudio-wt.js.map +0 -1
- package/speechflow-cli/dst/speechflow-util-webaudio.d.ts +0 -13
- package/speechflow-cli/dst/speechflow-util-webaudio.js +0 -137
- package/speechflow-cli/dst/speechflow-util-webaudio.js.map +0 -1
- /package/speechflow-cli/dst/{speechflow-util-webaudio-wt.d.ts → speechflow-node-a2a-pitch2-wt.d.ts} +0 -0
|
@@ -30,6 +30,10 @@ export default class SpeechFlowNodeXIOFile extends SpeechFlowNode {
|
|
|
30
30
|
chunkt: { type: "number", val: 65536, match: (n: number) => n >= 1024 && n <= 131072 }
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
+
/* sanity check parameters */
|
|
34
|
+
if (this.params.path === "")
|
|
35
|
+
throw new Error("required parameter \"path\" has to be given")
|
|
36
|
+
|
|
33
37
|
/* declare node input/output format */
|
|
34
38
|
if (this.params.mode === "rw") {
|
|
35
39
|
this.input = this.params.type
|
|
@@ -55,10 +59,6 @@ export default class SpeechFlowNodeXIOFile extends SpeechFlowNode {
|
|
|
55
59
|
) / (1000 / this.params.chunka)
|
|
56
60
|
const highWaterMarkText = this.params.chunkt
|
|
57
61
|
|
|
58
|
-
/* sanity check */
|
|
59
|
-
if (this.params.path === "")
|
|
60
|
-
throw new Error("required parameter \"path\" has to be given")
|
|
61
|
-
|
|
62
62
|
/* utility function: create a writable stream as chunker that
|
|
63
63
|
writes to process.stdout but properly handles finish events.
|
|
64
64
|
This ensures the writable side of the composed stream below
|
|
@@ -195,24 +195,25 @@ export default class SpeechFlowNodeXIOFile extends SpeechFlowNode {
|
|
|
195
195
|
async close () {
|
|
196
196
|
/* shutdown stream */
|
|
197
197
|
if (this.stream !== null) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
198
|
+
/* only destroy non-stdio streams */
|
|
199
|
+
if (this.params.path !== "-")
|
|
200
|
+
await util.destroyStream(this.stream)
|
|
201
|
+
else {
|
|
202
|
+
/* for stdio streams, just end without destroying */
|
|
203
|
+
const stream = this.stream
|
|
204
|
+
if ((stream instanceof Stream.Writable || stream instanceof Stream.Duplex) &&
|
|
205
|
+
(!stream.writableEnded && !stream.destroyed) ) {
|
|
206
|
+
await Promise.race([
|
|
207
|
+
new Promise<void>((resolve, reject) => {
|
|
208
|
+
stream.end((err?: Error) => {
|
|
205
209
|
if (err) reject(err)
|
|
206
210
|
else resolve()
|
|
207
211
|
})
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
])
|
|
214
|
-
if (this.params.path !== "-")
|
|
215
|
-
this.stream.destroy()
|
|
212
|
+
}),
|
|
213
|
+
util.timeoutPromise(5000)
|
|
214
|
+
])
|
|
215
|
+
}
|
|
216
|
+
}
|
|
216
217
|
this.stream = null
|
|
217
218
|
}
|
|
218
219
|
}
|
|
@@ -40,6 +40,18 @@ export default class SpeechFlowNodeXIOMQTT extends SpeechFlowNode {
|
|
|
40
40
|
type: { type: "string", pos: 6, val: "text", match: /^(?:audio|text)$/ }
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
+
/* sanity check parameters */
|
|
44
|
+
if (this.params.url === "")
|
|
45
|
+
throw new Error("required parameter \"url\" has to be given")
|
|
46
|
+
if ((this.params.mode === "w" || this.params.mode === "rw") && this.params.topicWrite === "")
|
|
47
|
+
throw new Error("writing to MQTT requires a topicWrite parameter")
|
|
48
|
+
if ((this.params.mode === "r" || this.params.mode === "rw") && this.params.topicRead === "")
|
|
49
|
+
throw new Error("reading from MQTT requires a topicRead parameter")
|
|
50
|
+
if (this.params.username !== "" && this.params.password === "")
|
|
51
|
+
throw new Error("username provided but password is missing")
|
|
52
|
+
if (this.params.username === "" && this.params.password !== "")
|
|
53
|
+
throw new Error("password provided but username is missing")
|
|
54
|
+
|
|
43
55
|
/* declare node input/output format */
|
|
44
56
|
if (this.params.mode === "rw") {
|
|
45
57
|
this.input = this.params.type
|
|
@@ -57,18 +69,6 @@ export default class SpeechFlowNodeXIOMQTT extends SpeechFlowNode {
|
|
|
57
69
|
|
|
58
70
|
/* open node */
|
|
59
71
|
async open () {
|
|
60
|
-
/* logical parameter sanity check */
|
|
61
|
-
if (this.params.url === "")
|
|
62
|
-
throw new Error("required parameter \"url\" has to be given")
|
|
63
|
-
if ((this.params.mode === "w" || this.params.mode === "rw") && this.params.topicWrite === "")
|
|
64
|
-
throw new Error("writing to MQTT requires a topicWrite parameter")
|
|
65
|
-
if ((this.params.mode === "r" || this.params.mode === "rw") && this.params.topicRead === "")
|
|
66
|
-
throw new Error("reading from MQTT requires a topicRead parameter")
|
|
67
|
-
if (this.params.username !== "" && this.params.password === "")
|
|
68
|
-
throw new Error("username provided but password is missing")
|
|
69
|
-
if (this.params.username === "" && this.params.password !== "")
|
|
70
|
-
throw new Error("password provided but username is missing")
|
|
71
|
-
|
|
72
72
|
/* connect remotely to a MQTT broker */
|
|
73
73
|
this.broker = MQTT.connect(this.params.url, {
|
|
74
74
|
protocolId: "MQTT",
|
|
@@ -158,9 +158,9 @@ export default class SpeechFlowNodeXIOMQTT extends SpeechFlowNode {
|
|
|
158
158
|
this.broker = null
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
/*
|
|
161
|
+
/* shutdown stream */
|
|
162
162
|
if (this.stream !== null) {
|
|
163
|
-
this.stream
|
|
163
|
+
await util.destroyStream(this.stream)
|
|
164
164
|
this.stream = null
|
|
165
165
|
}
|
|
166
166
|
}
|
|
@@ -36,6 +36,12 @@ export default class SpeechFlowNodeXIOWebSocket extends SpeechFlowNode {
|
|
|
36
36
|
type: { type: "string", val: "text", match: /^(?:audio|text)$/ }
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
+
/* sanity check parameters */
|
|
40
|
+
if (this.params.listen !== "" && this.params.connect !== "")
|
|
41
|
+
throw new Error("Websocket node cannot listen and connect at the same time")
|
|
42
|
+
else if (this.params.listen === "" && this.params.connect === "")
|
|
43
|
+
throw new Error("Websocket node requires either listen or connect mode")
|
|
44
|
+
|
|
39
45
|
/* declare node input/output format */
|
|
40
46
|
if (this.params.mode === "rw") {
|
|
41
47
|
this.input = this.params.type
|
|
@@ -53,12 +59,6 @@ export default class SpeechFlowNodeXIOWebSocket extends SpeechFlowNode {
|
|
|
53
59
|
|
|
54
60
|
/* open node */
|
|
55
61
|
async open () {
|
|
56
|
-
/* sanity check usage */
|
|
57
|
-
if (this.params.listen !== "" && this.params.connect !== "")
|
|
58
|
-
throw new Error("Websocket node cannot listen and connect at the same time")
|
|
59
|
-
else if (this.params.listen === "" && this.params.connect === "")
|
|
60
|
-
throw new Error("Websocket node requires either listen or connect mode")
|
|
61
|
-
|
|
62
62
|
if (this.params.listen !== "") {
|
|
63
63
|
/* listen locally on a Websocket port */
|
|
64
64
|
const url = new URL(this.params.listen)
|
|
@@ -136,8 +136,8 @@ export default class SpeechFlowNodeXIOWebSocket extends SpeechFlowNode {
|
|
|
136
136
|
}
|
|
137
137
|
Promise.all(results).then(() => {
|
|
138
138
|
callback()
|
|
139
|
-
}).catch((error:
|
|
140
|
-
callback(error)
|
|
139
|
+
}).catch((error: unknown) => {
|
|
140
|
+
callback(util.ensureError(error))
|
|
141
141
|
})
|
|
142
142
|
}
|
|
143
143
|
},
|
|
@@ -236,13 +236,13 @@ export default class SpeechFlowNodeXIOWebSocket extends SpeechFlowNode {
|
|
|
236
236
|
|
|
237
237
|
/* close Websocket client */
|
|
238
238
|
if (this.client !== null) {
|
|
239
|
-
this.client
|
|
239
|
+
this.client.close()
|
|
240
240
|
this.client = null
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
/*
|
|
243
|
+
/* shutdown stream */
|
|
244
244
|
if (this.stream !== null) {
|
|
245
|
-
this.stream
|
|
245
|
+
await util.destroyStream(this.stream)
|
|
246
246
|
this.stream = null
|
|
247
247
|
}
|
|
248
248
|
}
|
|
@@ -44,12 +44,12 @@ export default class SpeechFlowNode extends Events.EventEmitter {
|
|
|
44
44
|
|
|
45
45
|
/* general constant configuration (for reference) */
|
|
46
46
|
config = {
|
|
47
|
-
audioChannels: 1, /*
|
|
48
|
-
audioBitDepth: 16 as (1 | 8 | 16 | 24 | 32), /*
|
|
49
|
-
audioLittleEndian: true, /*
|
|
50
|
-
audioSampleRate: 48000, /*
|
|
51
|
-
textEncoding: "utf8" as BufferEncoding, /*
|
|
52
|
-
cacheDir: "" /*
|
|
47
|
+
audioChannels: 1, /* audio mono channel */
|
|
48
|
+
audioBitDepth: 16 as (1 | 8 | 16 | 24 | 32), /* audio PCM 16-bit integer */
|
|
49
|
+
audioLittleEndian: true, /* audio PCM little-endian */
|
|
50
|
+
audioSampleRate: 48000, /* audio 48kHz sample rate */
|
|
51
|
+
textEncoding: "utf8" as BufferEncoding, /* UTF-8 text encoding */
|
|
52
|
+
cacheDir: "" /* directory for cache files */
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/* announced information */
|
|
@@ -132,6 +132,36 @@ export async function processInt16ArrayInSegments (
|
|
|
132
132
|
return data
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/* update envelope (smoothed amplitude contour) for single channel */
|
|
136
|
+
export function updateEnvelopeForChannel(
|
|
137
|
+
env: number[],
|
|
138
|
+
sampleRate: number,
|
|
139
|
+
chan: number,
|
|
140
|
+
samples: Float32Array,
|
|
141
|
+
attack: number,
|
|
142
|
+
release: number
|
|
143
|
+
): number {
|
|
144
|
+
/* fetch old envelope value */
|
|
145
|
+
if (env[chan] === undefined)
|
|
146
|
+
env[chan] = 1e-12
|
|
147
|
+
let currentEnv = env[chan]
|
|
148
|
+
|
|
149
|
+
/* calculate attack/release alpha values */
|
|
150
|
+
const alphaA = Math.exp(-1 / (attack * sampleRate))
|
|
151
|
+
const alphaR = Math.exp(-1 / (release * sampleRate))
|
|
152
|
+
|
|
153
|
+
/* iterate over all samples and calculate RMS */
|
|
154
|
+
for (const s of samples) {
|
|
155
|
+
const x = Math.abs(s)
|
|
156
|
+
const det = x * x
|
|
157
|
+
if (det > currentEnv)
|
|
158
|
+
currentEnv = alphaA * currentEnv + (1 - alphaA) * det
|
|
159
|
+
else
|
|
160
|
+
currentEnv = alphaR * currentEnv + (1 - alphaR) * det
|
|
161
|
+
}
|
|
162
|
+
return Math.sqrt(Math.max(currentEnv, 1e-12))
|
|
163
|
+
}
|
|
164
|
+
|
|
135
165
|
/* helper functions for linear/decibel conversions */
|
|
136
166
|
export function lin2dB (x: number): number {
|
|
137
167
|
return 20 * Math.log10(Math.max(x, 1e-12))
|
|
@@ -258,7 +288,7 @@ export class WebAudio {
|
|
|
258
288
|
this.pendingPromises.clear()
|
|
259
289
|
}
|
|
260
290
|
catch (_err) {
|
|
261
|
-
/*
|
|
291
|
+
/* ignored -- cleanup during shutdown */
|
|
262
292
|
}
|
|
263
293
|
|
|
264
294
|
/* disconnect nodes */
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/* helper function for promise-based timeout */
|
|
8
|
-
export function timeoutPromise (duration: number = 10 * 1000, info = "timeout") {
|
|
9
|
-
return new Promise<
|
|
8
|
+
export function timeoutPromise<T = void> (duration: number = 10 * 1000, info = "timeout") {
|
|
9
|
+
return new Promise<T>((resolve, reject) => {
|
|
10
10
|
setTimeout(() => { reject(new Error(info)) }, duration)
|
|
11
11
|
})
|
|
12
12
|
}
|
|
@@ -21,7 +21,13 @@ export function ensureError (error: unknown, prefix?: string, debug = false): Er
|
|
|
21
21
|
msg = `${prefix}: ${msg}`
|
|
22
22
|
if (debug && error instanceof Error)
|
|
23
23
|
msg = `${msg}\n${error.stack}`
|
|
24
|
-
|
|
24
|
+
if (error instanceof Error) {
|
|
25
|
+
const err = new Error(msg, { cause: error })
|
|
26
|
+
err.stack = error.stack
|
|
27
|
+
return err
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
return new Error(msg)
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
/* helper function for retrieving a Promise object */
|
|
@@ -36,12 +36,12 @@ export function createTransformStreamForWritableSide () {
|
|
|
36
36
|
|
|
37
37
|
/* create a Duplex/Transform stream which has
|
|
38
38
|
object-mode on Readable side and buffer/string-mode on Writable side */
|
|
39
|
-
export function createTransformStreamForReadableSide (type: "text" | "audio", getTimeZero: () => DateTime) {
|
|
39
|
+
export function createTransformStreamForReadableSide (type: "text" | "audio", getTimeZero: () => DateTime, highWaterMark?: number) {
|
|
40
40
|
return new Stream.Transform({
|
|
41
41
|
readableObjectMode: true,
|
|
42
42
|
writableObjectMode: true,
|
|
43
43
|
decodeStrings: false,
|
|
44
|
-
highWaterMark: (type === "audio" ? 19200
|
|
44
|
+
highWaterMark: highWaterMark ?? (type === "audio" ? 19200 /* 400ms */: 65536 /* 64KB */),
|
|
45
45
|
transform (chunk: Buffer | string, encoding, callback) {
|
|
46
46
|
if (chunk === null) {
|
|
47
47
|
this.push(null)
|
|
@@ -88,7 +88,8 @@ type SpeechFlowChunkSerialized = {
|
|
|
88
88
|
timestampEnd: number,
|
|
89
89
|
kind: string,
|
|
90
90
|
type: string,
|
|
91
|
-
payload: Uint8Array
|
|
91
|
+
payload: Uint8Array,
|
|
92
|
+
meta?: Array<[ string, any ]>
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
/* encode/serialize chunk of data */
|
|
@@ -100,13 +101,15 @@ export function streamChunkEncode (chunk: SpeechFlowChunk) {
|
|
|
100
101
|
const encoder = new TextEncoder()
|
|
101
102
|
payload = encoder.encode(chunk.payload)
|
|
102
103
|
}
|
|
103
|
-
const data = {
|
|
104
|
+
const data: SpeechFlowChunkSerialized = {
|
|
104
105
|
timestampStart: chunk.timestampStart.toMillis(),
|
|
105
106
|
timestampEnd: chunk.timestampEnd.toMillis(),
|
|
106
107
|
kind: chunk.kind,
|
|
107
108
|
type: chunk.type,
|
|
108
109
|
payload
|
|
109
|
-
}
|
|
110
|
+
}
|
|
111
|
+
if (chunk.meta.size > 0)
|
|
112
|
+
data.meta = Array.from(chunk.meta.entries())
|
|
110
113
|
const _data = CBOR.encode(data)
|
|
111
114
|
return _data
|
|
112
115
|
}
|
|
@@ -130,7 +133,8 @@ export function streamChunkDecode (_data: Uint8Array) {
|
|
|
130
133
|
Duration.fromMillis(data.timestampEnd),
|
|
131
134
|
data.kind as "intermediate" | "final",
|
|
132
135
|
data.type as "audio" | "text",
|
|
133
|
-
payload
|
|
136
|
+
payload,
|
|
137
|
+
data.meta ? new Map(data.meta) : undefined
|
|
134
138
|
)
|
|
135
139
|
return chunk
|
|
136
140
|
}
|
|
@@ -195,3 +199,24 @@ export class StreamWrapper extends Stream.Transform {
|
|
|
195
199
|
super._destroy(error, callback)
|
|
196
200
|
}
|
|
197
201
|
}
|
|
202
|
+
|
|
203
|
+
/* helper function for destruction of a stream */
|
|
204
|
+
export async function destroyStream(
|
|
205
|
+
stream: Stream.Readable | Stream.Writable | Stream.Duplex | Stream.Transform
|
|
206
|
+
) {
|
|
207
|
+
/* signal the end for a writable stream */
|
|
208
|
+
if ((stream instanceof Stream.Duplex ||
|
|
209
|
+
stream instanceof Stream.Transform ||
|
|
210
|
+
stream instanceof Stream.Writable ) &&
|
|
211
|
+
(!stream.writableEnded &&
|
|
212
|
+
!stream.destroyed ) )
|
|
213
|
+
await Promise.race([
|
|
214
|
+
new Promise<void>((resolve) => {
|
|
215
|
+
stream.end(() => { resolve() })
|
|
216
|
+
}),
|
|
217
|
+
util.timeoutPromise(5000, "stream end timeout")
|
|
218
|
+
])
|
|
219
|
+
|
|
220
|
+
/* destroy the stream */
|
|
221
|
+
stream.destroy()
|
|
222
|
+
}
|