speechflow 1.6.5 → 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 +12 -0
- package/README.md +23 -0
- package/etc/stx.conf +5 -0
- package/package.json +3 -3
- 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.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 +2 -8
- 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 +38 -34
- 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 +11 -11
- 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.d.ts +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 +12 -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 +24 -23
- 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 -1
- package/speechflow-cli/dst/speechflow-node-a2t-openai.js +15 -15
- package/speechflow-cli/dst/speechflow-node-a2t-openai.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.js +9 -9
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js +13 -12
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js +4 -4
- 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 +2 -2
- 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 +2 -2
- 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 +3 -2
- 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.js +1 -1
- package/speechflow-cli/dst/speechflow-util-audio.js.map +1 -1
- package/speechflow-cli/dst/speechflow-util-stream.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-util-stream.js +22 -2
- package/speechflow-cli/dst/speechflow-util-stream.js.map +1 -1
- package/speechflow-cli/etc/tsconfig.json +1 -0
- package/speechflow-cli/package.json +14 -14
- package/speechflow-cli/src/speechflow-node-a2a-compressor.ts +13 -12
- package/speechflow-cli/src/speechflow-node-a2a-expander.ts +13 -12
- package/speechflow-cli/src/speechflow-node-a2a-ffmpeg.ts +2 -8
- 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 +42 -36
- package/speechflow-cli/src/speechflow-node-a2a-meter.ts +11 -11
- 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 +13 -12
- package/speechflow-cli/src/speechflow-node-a2a-speex.ts +14 -13
- package/speechflow-cli/src/speechflow-node-a2a-vad.ts +24 -23
- 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 -15
- package/speechflow-cli/src/speechflow-node-t2a-amazon.ts +9 -9
- package/speechflow-cli/src/speechflow-node-t2a-elevenlabs.ts +13 -12
- package/speechflow-cli/src/speechflow-node-t2a-kokoro.ts +4 -4
- 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 +2 -2
- 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 +2 -2
- 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 +4 -3
- 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 +10 -10
- package/speechflow-cli/src/speechflow-node.ts +6 -6
- package/speechflow-cli/src/speechflow-util-audio.ts +1 -1
- package/speechflow-cli/src/speechflow-util-stream.ts +30 -5
- package/speechflow-ui-db/dst/index.js +20 -20
- package/speechflow-ui-db/package.json +7 -7
- package/speechflow-ui-st/dst/index.js +40 -40
- package/speechflow-ui-st/package.json +8 -8
|
@@ -234,9 +234,9 @@ export default class SpeechFlowNodeT2TOpenAI extends SpeechFlowNode {
|
|
|
234
234
|
|
|
235
235
|
/* close node */
|
|
236
236
|
async close () {
|
|
237
|
-
/*
|
|
237
|
+
/* shutdown stream */
|
|
238
238
|
if (this.stream !== null) {
|
|
239
|
-
this.stream
|
|
239
|
+
await util.destroyStream(this.stream)
|
|
240
240
|
this.stream = null
|
|
241
241
|
}
|
|
242
242
|
|
|
@@ -33,7 +33,7 @@ export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
|
33
33
|
private queueRecv = this.queue.pointerUse("recv")
|
|
34
34
|
private queueSplit = this.queue.pointerUse("split")
|
|
35
35
|
private queueSend = this.queue.pointerUse("send")
|
|
36
|
-
private
|
|
36
|
+
private closing = false
|
|
37
37
|
private workingOffTimer: ReturnType<typeof setTimeout> | null = null
|
|
38
38
|
|
|
39
39
|
/* construct node */
|
|
@@ -51,12 +51,12 @@ export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
|
51
51
|
/* open node */
|
|
52
52
|
async open () {
|
|
53
53
|
/* clear destruction flag */
|
|
54
|
-
this.
|
|
54
|
+
this.closing = false
|
|
55
55
|
|
|
56
56
|
/* work off queued text frames */
|
|
57
57
|
let workingOff = false
|
|
58
58
|
const workOffQueue = async () => {
|
|
59
|
-
if (this.
|
|
59
|
+
if (this.closing)
|
|
60
60
|
return
|
|
61
61
|
|
|
62
62
|
/* control working off round */
|
|
@@ -70,7 +70,7 @@ export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
|
70
70
|
this.queue.off("write", workOffQueue)
|
|
71
71
|
|
|
72
72
|
/* try to work off one or more chunks */
|
|
73
|
-
while (!this.
|
|
73
|
+
while (!this.closing) {
|
|
74
74
|
const element = this.queueSplit.peek()
|
|
75
75
|
if (element === undefined)
|
|
76
76
|
break
|
|
@@ -134,7 +134,7 @@ export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
|
134
134
|
|
|
135
135
|
/* re-initiate working off round (if still not destroyed) */
|
|
136
136
|
workingOff = false
|
|
137
|
-
if (!this.
|
|
137
|
+
if (!this.closing) {
|
|
138
138
|
this.workingOffTimer = setTimeout(workOffQueue, 100)
|
|
139
139
|
this.queue.once("write", workOffQueue)
|
|
140
140
|
}
|
|
@@ -151,7 +151,7 @@ export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
|
151
151
|
|
|
152
152
|
/* receive text chunk (writable side of stream) */
|
|
153
153
|
write (chunk: SpeechFlowChunk, encoding, callback) {
|
|
154
|
-
if (self.
|
|
154
|
+
if (self.closing)
|
|
155
155
|
callback(new Error("stream already destroyed"))
|
|
156
156
|
else if (Buffer.isBuffer(chunk.payload))
|
|
157
157
|
callback(new Error("expected text input as string chunks"))
|
|
@@ -166,7 +166,7 @@ export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
|
166
166
|
|
|
167
167
|
/* receive no more text chunks (writable side of stream) */
|
|
168
168
|
final (callback) {
|
|
169
|
-
if (self.
|
|
169
|
+
if (self.closing) {
|
|
170
170
|
callback()
|
|
171
171
|
return
|
|
172
172
|
}
|
|
@@ -179,7 +179,7 @@ export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
|
179
179
|
read (_size) {
|
|
180
180
|
/* flush pending text chunks */
|
|
181
181
|
const flushPendingChunks = () => {
|
|
182
|
-
if (self.
|
|
182
|
+
if (self.closing) {
|
|
183
183
|
this.push(null)
|
|
184
184
|
return
|
|
185
185
|
}
|
|
@@ -210,7 +210,7 @@ export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
|
210
210
|
self.queue.trim()
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
-
else if (!self.
|
|
213
|
+
else if (!self.closing)
|
|
214
214
|
self.queue.once("write", flushPendingChunks)
|
|
215
215
|
}
|
|
216
216
|
flushPendingChunks()
|
|
@@ -220,8 +220,8 @@ export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
|
220
220
|
|
|
221
221
|
/* close node */
|
|
222
222
|
async close () {
|
|
223
|
-
/* indicate
|
|
224
|
-
this.
|
|
223
|
+
/* indicate closing */
|
|
224
|
+
this.closing = true
|
|
225
225
|
|
|
226
226
|
/* clean up timer */
|
|
227
227
|
if (this.workingOffTimer !== null) {
|
|
@@ -232,9 +232,9 @@ export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
|
232
232
|
/* remove any pending event listeners */
|
|
233
233
|
this.queue.removeAllListeners("write")
|
|
234
234
|
|
|
235
|
-
/*
|
|
235
|
+
/* shutdown stream */
|
|
236
236
|
if (this.stream !== null) {
|
|
237
|
-
this.stream
|
|
237
|
+
await util.destroyStream(this.stream)
|
|
238
238
|
this.stream = null
|
|
239
239
|
}
|
|
240
240
|
}
|
|
@@ -257,9 +257,9 @@ export default class SpeechFlowNodeT2TSubtitle extends SpeechFlowNode {
|
|
|
257
257
|
|
|
258
258
|
/* close node */
|
|
259
259
|
async close () {
|
|
260
|
-
/*
|
|
260
|
+
/* shutdown stream */
|
|
261
261
|
if (this.stream !== null) {
|
|
262
|
-
this.stream
|
|
262
|
+
await util.destroyStream(this.stream)
|
|
263
263
|
this.stream = null
|
|
264
264
|
}
|
|
265
265
|
|
|
@@ -227,9 +227,9 @@ export default class SpeechFlowNodeT2TTransformers extends SpeechFlowNode {
|
|
|
227
227
|
|
|
228
228
|
/* close node */
|
|
229
229
|
async close () {
|
|
230
|
-
/*
|
|
230
|
+
/* shutdown stream */
|
|
231
231
|
if (this.stream !== null) {
|
|
232
|
-
this.stream
|
|
232
|
+
await util.destroyStream(this.stream)
|
|
233
233
|
this.stream = null
|
|
234
234
|
}
|
|
235
235
|
|
|
@@ -125,9 +125,9 @@ export default class SpeechFlowNodeX2XFilter extends SpeechFlowNode {
|
|
|
125
125
|
|
|
126
126
|
/* close node */
|
|
127
127
|
async close () {
|
|
128
|
-
/*
|
|
128
|
+
/* shutdown stream */
|
|
129
129
|
if (this.stream !== null) {
|
|
130
|
-
this.stream
|
|
130
|
+
await util.destroyStream(this.stream)
|
|
131
131
|
this.stream = null
|
|
132
132
|
}
|
|
133
133
|
}
|
|
@@ -12,6 +12,7 @@ import { Duration } from "luxon"
|
|
|
12
12
|
|
|
13
13
|
/* internal dependencies */
|
|
14
14
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
15
|
+
import * as util from "./speechflow-util"
|
|
15
16
|
|
|
16
17
|
/* SpeechFlow node for data flow tracing */
|
|
17
18
|
export default class SpeechFlowNodeX2XTrace extends SpeechFlowNode {
|
|
@@ -19,7 +20,7 @@ export default class SpeechFlowNodeX2XTrace extends SpeechFlowNode {
|
|
|
19
20
|
public static name = "x2x-trace"
|
|
20
21
|
|
|
21
22
|
/* internal state */
|
|
22
|
-
private
|
|
23
|
+
private closing = false
|
|
23
24
|
|
|
24
25
|
/* construct node */
|
|
25
26
|
constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
|
|
@@ -56,7 +57,7 @@ export default class SpeechFlowNodeX2XTrace extends SpeechFlowNode {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
/* clear destruction flag */
|
|
59
|
-
this.
|
|
60
|
+
this.closing = false
|
|
60
61
|
|
|
61
62
|
/* helper functions for formatting */
|
|
62
63
|
const fmtTime = (t: Duration) => t.toFormat("hh:mm:ss.SSS")
|
|
@@ -84,7 +85,7 @@ export default class SpeechFlowNodeX2XTrace extends SpeechFlowNode {
|
|
|
84
85
|
highWaterMark: 1,
|
|
85
86
|
transform (chunk: SpeechFlowChunk, encoding, callback) {
|
|
86
87
|
let error: Error | undefined
|
|
87
|
-
if (self.
|
|
88
|
+
if (self.closing) {
|
|
88
89
|
callback(new Error("stream already destroyed"))
|
|
89
90
|
return
|
|
90
91
|
}
|
|
@@ -118,7 +119,7 @@ export default class SpeechFlowNodeX2XTrace extends SpeechFlowNode {
|
|
|
118
119
|
}
|
|
119
120
|
},
|
|
120
121
|
final (callback) {
|
|
121
|
-
if (self.
|
|
122
|
+
if (self.closing || self.params.mode === "sink") {
|
|
122
123
|
callback()
|
|
123
124
|
return
|
|
124
125
|
}
|
|
@@ -130,13 +131,13 @@ export default class SpeechFlowNodeX2XTrace extends SpeechFlowNode {
|
|
|
130
131
|
|
|
131
132
|
/* close node */
|
|
132
133
|
async close () {
|
|
133
|
-
/*
|
|
134
|
+
/* indicate closing */
|
|
135
|
+
this.closing = true
|
|
136
|
+
|
|
137
|
+
/* shutdown stream */
|
|
134
138
|
if (this.stream !== null) {
|
|
135
|
-
this.stream
|
|
139
|
+
await util.destroyStream(this.stream)
|
|
136
140
|
this.stream = null
|
|
137
141
|
}
|
|
138
|
-
|
|
139
|
-
/* indicate destruction */
|
|
140
|
-
this.destroyed = true
|
|
141
142
|
}
|
|
142
143
|
}
|
|
@@ -36,6 +36,10 @@ export default class SpeechFlowNodeXIODevice extends SpeechFlowNode {
|
|
|
36
36
|
chunk: { type: "number", pos: 2, val: 200, match: (n: number) => n >= 10 && n <= 1000 }
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
+
/* sanity check parameters */
|
|
40
|
+
if (this.params.device === "")
|
|
41
|
+
throw new Error("required parameter \"device\" has to be given")
|
|
42
|
+
|
|
39
43
|
/* declare node input/output format */
|
|
40
44
|
if (this.params.mode === "rw") {
|
|
41
45
|
this.input = "audio"
|
|
@@ -163,9 +167,6 @@ export default class SpeechFlowNodeXIODevice extends SpeechFlowNode {
|
|
|
163
167
|
|
|
164
168
|
/* open node */
|
|
165
169
|
async open () {
|
|
166
|
-
if (this.params.device === "")
|
|
167
|
-
throw new Error("required parameter \"device\" has to be given")
|
|
168
|
-
|
|
169
170
|
/* determine device */
|
|
170
171
|
const device = this.audioDeviceFromURL(this.params.mode, this.params.device)
|
|
171
172
|
|
|
@@ -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
|
},
|
|
@@ -240,9 +240,9 @@ export default class SpeechFlowNodeXIOWebSocket extends SpeechFlowNode {
|
|
|
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 */
|
|
@@ -41,7 +41,7 @@ export function createTransformStreamForReadableSide (type: "text" | "audio", ge
|
|
|
41
41
|
readableObjectMode: true,
|
|
42
42
|
writableObjectMode: true,
|
|
43
43
|
decodeStrings: false,
|
|
44
|
-
highWaterMark: 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
|
+
}
|