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
|
@@ -26,8 +26,7 @@ export default class SpeechFlowNodeT2AAmazon extends SpeechFlowNode {
|
|
|
26
26
|
|
|
27
27
|
/* internal state */
|
|
28
28
|
private client: PollyClient | null = null
|
|
29
|
-
private
|
|
30
|
-
private destroyed = false
|
|
29
|
+
private closing = false
|
|
31
30
|
private resampler: SpeexResampler | null = null
|
|
32
31
|
|
|
33
32
|
/* construct node */
|
|
@@ -62,7 +61,7 @@ export default class SpeechFlowNodeT2AAmazon extends SpeechFlowNode {
|
|
|
62
61
|
/* open node */
|
|
63
62
|
async open () {
|
|
64
63
|
/* clear destruction flag */
|
|
65
|
-
this.
|
|
64
|
+
this.closing = false
|
|
66
65
|
|
|
67
66
|
/* establish AWS Polly connection */
|
|
68
67
|
this.client = new PollyClient({
|
|
@@ -114,11 +113,6 @@ export default class SpeechFlowNodeT2AAmazon extends SpeechFlowNode {
|
|
|
114
113
|
|
|
115
114
|
/* establish resampler from AWS Polly's maximum 16Khz output
|
|
116
115
|
(for PCM output) to our standard audio sample rate (48KHz) */
|
|
117
|
-
if (!SpeechFlowNodeT2AAmazon.speexInitialized) {
|
|
118
|
-
/* at least once initialize resampler */
|
|
119
|
-
await SpeexResampler.initPromise
|
|
120
|
-
SpeechFlowNodeT2AAmazon.speexInitialized = true
|
|
121
|
-
}
|
|
122
116
|
this.resampler = new SpeexResampler(1, 16000, this.config.audioSampleRate, 7)
|
|
123
117
|
|
|
124
118
|
/* create transform stream and connect it to the AWS Polly API */
|
|
@@ -129,7 +123,7 @@ export default class SpeechFlowNodeT2AAmazon extends SpeechFlowNode {
|
|
|
129
123
|
decodeStrings: false,
|
|
130
124
|
highWaterMark: 1,
|
|
131
125
|
transform (chunk: SpeechFlowChunk, encoding, callback) {
|
|
132
|
-
if (self.
|
|
126
|
+
if (self.closing) {
|
|
133
127
|
callback(new Error("stream already destroyed"))
|
|
134
128
|
return
|
|
135
129
|
}
|
|
@@ -138,7 +132,7 @@ export default class SpeechFlowNodeT2AAmazon extends SpeechFlowNode {
|
|
|
138
132
|
else if (chunk.payload.length > 0) {
|
|
139
133
|
self.log("debug", `send data (${chunk.payload.length} bytes): "${chunk.payload}"`)
|
|
140
134
|
textToSpeech(chunk.payload as string).then((buffer) => {
|
|
141
|
-
if (self.
|
|
135
|
+
if (self.closing)
|
|
142
136
|
throw new Error("stream destroyed during processing")
|
|
143
137
|
const chunkNew = chunk.clone()
|
|
144
138
|
chunkNew.type = "audio"
|
|
@@ -153,7 +147,7 @@ export default class SpeechFlowNodeT2AAmazon extends SpeechFlowNode {
|
|
|
153
147
|
callback()
|
|
154
148
|
},
|
|
155
149
|
final (callback) {
|
|
156
|
-
if (self.
|
|
150
|
+
if (self.closing) {
|
|
157
151
|
callback()
|
|
158
152
|
return
|
|
159
153
|
}
|
|
@@ -165,8 +159,8 @@ export default class SpeechFlowNodeT2AAmazon extends SpeechFlowNode {
|
|
|
165
159
|
|
|
166
160
|
/* close node */
|
|
167
161
|
async close () {
|
|
168
|
-
/* indicate
|
|
169
|
-
this.
|
|
162
|
+
/* indicate closing */
|
|
163
|
+
this.closing = true
|
|
170
164
|
|
|
171
165
|
/* destroy resampler */
|
|
172
166
|
if (this.resampler !== null)
|
|
@@ -177,9 +171,9 @@ export default class SpeechFlowNodeT2AAmazon extends SpeechFlowNode {
|
|
|
177
171
|
this.client.destroy()
|
|
178
172
|
this.client = null
|
|
179
173
|
}
|
|
180
|
-
/*
|
|
174
|
+
/* shutdown stream */
|
|
181
175
|
if (this.stream !== null) {
|
|
182
|
-
this.stream
|
|
176
|
+
await util.destroyStream(this.stream)
|
|
183
177
|
this.stream = null
|
|
184
178
|
}
|
|
185
179
|
}
|
|
@@ -14,6 +14,7 @@ import SpeexResampler from "speex-resampler"
|
|
|
14
14
|
|
|
15
15
|
/* internal dependencies */
|
|
16
16
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
17
|
+
import * as util from "./speechflow-util"
|
|
17
18
|
|
|
18
19
|
/* SpeechFlow node for Elevenlabs text-to-speech conversion */
|
|
19
20
|
export default class SpeechFlowNodeT2AElevenlabs extends SpeechFlowNode {
|
|
@@ -22,8 +23,7 @@ export default class SpeechFlowNodeT2AElevenlabs extends SpeechFlowNode {
|
|
|
22
23
|
|
|
23
24
|
/* internal state */
|
|
24
25
|
private elevenlabs: ElevenLabs.ElevenLabsClient | null = null
|
|
25
|
-
private
|
|
26
|
-
private destroyed = false
|
|
26
|
+
private closing = false
|
|
27
27
|
private resampler: SpeexResampler | null = null
|
|
28
28
|
|
|
29
29
|
/* construct node */
|
|
@@ -68,7 +68,7 @@ export default class SpeechFlowNodeT2AElevenlabs extends SpeechFlowNode {
|
|
|
68
68
|
/* open node */
|
|
69
69
|
async open () {
|
|
70
70
|
/* clear destruction flag */
|
|
71
|
-
this.
|
|
71
|
+
this.closing = false
|
|
72
72
|
|
|
73
73
|
/* establish ElevenLabs API connection */
|
|
74
74
|
this.elevenlabs = new ElevenLabs.ElevenLabsClient({
|
|
@@ -117,7 +117,7 @@ export default class SpeechFlowNodeT2AElevenlabs extends SpeechFlowNode {
|
|
|
117
117
|
modelId: model,
|
|
118
118
|
languageCode: this.params.language,
|
|
119
119
|
outputFormat: `pcm_${maxSampleRate}` as ElevenLabs.ElevenLabs.OutputFormat,
|
|
120
|
-
seed: 815, /*
|
|
120
|
+
seed: 815, /* arbitrary, but fixated by us */
|
|
121
121
|
voiceSettings: {
|
|
122
122
|
speed: this.params.speed,
|
|
123
123
|
stability: this.params.stability,
|
|
@@ -131,11 +131,6 @@ export default class SpeechFlowNodeT2AElevenlabs extends SpeechFlowNode {
|
|
|
131
131
|
|
|
132
132
|
/* establish resampler from ElevenLabs's maximum 24Khz
|
|
133
133
|
output to our standard audio sample rate (48KHz) */
|
|
134
|
-
if (!SpeechFlowNodeT2AElevenlabs.speexInitialized) {
|
|
135
|
-
/* at least once initialize resampler */
|
|
136
|
-
await SpeexResampler.initPromise
|
|
137
|
-
SpeechFlowNodeT2AElevenlabs.speexInitialized = true
|
|
138
|
-
}
|
|
139
134
|
this.resampler = new SpeexResampler(1, maxSampleRate, this.config.audioSampleRate, 7)
|
|
140
135
|
|
|
141
136
|
/* create transform stream and connect it to the ElevenLabs API */
|
|
@@ -146,7 +141,7 @@ export default class SpeechFlowNodeT2AElevenlabs extends SpeechFlowNode {
|
|
|
146
141
|
decodeStrings: false,
|
|
147
142
|
highWaterMark: 1,
|
|
148
143
|
transform (chunk: SpeechFlowChunk, encoding, callback) {
|
|
149
|
-
if (self.
|
|
144
|
+
if (self.closing)
|
|
150
145
|
callback(new Error("stream already destroyed"))
|
|
151
146
|
else if (Buffer.isBuffer(chunk.payload))
|
|
152
147
|
callback(new Error("invalid chunk payload type"))
|
|
@@ -163,14 +158,14 @@ export default class SpeechFlowNodeT2AElevenlabs extends SpeechFlowNode {
|
|
|
163
158
|
}
|
|
164
159
|
}
|
|
165
160
|
try {
|
|
166
|
-
if (self.
|
|
161
|
+
if (self.closing) {
|
|
167
162
|
clearProcessTimeout()
|
|
168
163
|
callback(new Error("stream destroyed during processing"))
|
|
169
164
|
return
|
|
170
165
|
}
|
|
171
166
|
const stream = await speechStream(chunk.payload as string)
|
|
172
167
|
const buffer = await getStreamAsBuffer(stream)
|
|
173
|
-
if (self.
|
|
168
|
+
if (self.closing) {
|
|
174
169
|
clearProcessTimeout()
|
|
175
170
|
callback(new Error("stream destroyed during processing"))
|
|
176
171
|
return
|
|
@@ -186,13 +181,13 @@ export default class SpeechFlowNodeT2AElevenlabs extends SpeechFlowNode {
|
|
|
186
181
|
}
|
|
187
182
|
catch (error) {
|
|
188
183
|
clearProcessTimeout()
|
|
189
|
-
callback(error
|
|
184
|
+
callback(util.ensureError(error, "ElevenLabs processing failed"))
|
|
190
185
|
}
|
|
191
186
|
})()
|
|
192
187
|
}
|
|
193
188
|
},
|
|
194
189
|
final (callback) {
|
|
195
|
-
if (self.
|
|
190
|
+
if (self.closing) {
|
|
196
191
|
callback()
|
|
197
192
|
return
|
|
198
193
|
}
|
|
@@ -204,12 +199,12 @@ export default class SpeechFlowNodeT2AElevenlabs extends SpeechFlowNode {
|
|
|
204
199
|
|
|
205
200
|
/* close node */
|
|
206
201
|
async close () {
|
|
207
|
-
/* indicate
|
|
208
|
-
this.
|
|
202
|
+
/* indicate closing */
|
|
203
|
+
this.closing = true
|
|
209
204
|
|
|
210
|
-
/*
|
|
205
|
+
/* shutdown stream */
|
|
211
206
|
if (this.stream !== null) {
|
|
212
|
-
this.stream
|
|
207
|
+
await util.destroyStream(this.stream)
|
|
213
208
|
this.stream = null
|
|
214
209
|
}
|
|
215
210
|
|
|
@@ -23,7 +23,6 @@ export default class SpeechFlowNodeT2AKokoro extends SpeechFlowNode {
|
|
|
23
23
|
/* internal state */
|
|
24
24
|
private kokoro: KokoroTTS | null = null
|
|
25
25
|
private resampler: SpeexResampler | null = null
|
|
26
|
-
private static speexInitialized = false
|
|
27
26
|
|
|
28
27
|
/* construct node */
|
|
29
28
|
constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
|
|
@@ -82,11 +81,6 @@ export default class SpeechFlowNodeT2AKokoro extends SpeechFlowNode {
|
|
|
82
81
|
|
|
83
82
|
/* establish resampler from Kokoro's maximum 24Khz
|
|
84
83
|
output to our standard audio sample rate (48KHz) */
|
|
85
|
-
if (!SpeechFlowNodeT2AKokoro.speexInitialized) {
|
|
86
|
-
/* at least once initialize resampler */
|
|
87
|
-
SpeechFlowNodeT2AKokoro.speexInitialized = true
|
|
88
|
-
await SpeexResampler.initPromise
|
|
89
|
-
}
|
|
90
84
|
this.resampler = new SpeexResampler(1, 24000, this.config.audioSampleRate, 7)
|
|
91
85
|
|
|
92
86
|
/* determine voice for text-to-speech operation */
|
|
@@ -125,7 +119,7 @@ export default class SpeechFlowNodeT2AKokoro extends SpeechFlowNode {
|
|
|
125
119
|
}
|
|
126
120
|
|
|
127
121
|
/* create transform stream and connect it to the Kokoro API */
|
|
128
|
-
const
|
|
122
|
+
const self = this
|
|
129
123
|
this.stream = new Stream.Transform({
|
|
130
124
|
writableObjectMode: true,
|
|
131
125
|
readableObjectMode: true,
|
|
@@ -136,7 +130,7 @@ export default class SpeechFlowNodeT2AKokoro extends SpeechFlowNode {
|
|
|
136
130
|
callback(new Error("invalid chunk payload type"))
|
|
137
131
|
else {
|
|
138
132
|
text2speech(chunk.payload).then((buffer) => {
|
|
139
|
-
log("info", `Kokoro: received audio (buffer length: ${buffer.byteLength})`)
|
|
133
|
+
self.log("info", `Kokoro: received audio (buffer length: ${buffer.byteLength})`)
|
|
140
134
|
chunk = chunk.clone()
|
|
141
135
|
chunk.type = "audio"
|
|
142
136
|
chunk.payload = buffer
|
|
@@ -156,9 +150,9 @@ export default class SpeechFlowNodeT2AKokoro extends SpeechFlowNode {
|
|
|
156
150
|
|
|
157
151
|
/* close node */
|
|
158
152
|
async close () {
|
|
159
|
-
/*
|
|
153
|
+
/* shutdown stream */
|
|
160
154
|
if (this.stream !== null) {
|
|
161
|
-
this.stream
|
|
155
|
+
await util.destroyStream(this.stream)
|
|
162
156
|
this.stream = null
|
|
163
157
|
}
|
|
164
158
|
|
|
@@ -101,7 +101,7 @@ export default class SpeechFlowNodeT2TAmazon extends SpeechFlowNode {
|
|
|
101
101
|
await new Promise((resolve) => setTimeout(resolve, delayMs))
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
throw
|
|
104
|
+
throw util.ensureError(lastError)
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/* establish a duplex stream and connect it to AWS Translate */
|
|
@@ -143,9 +143,9 @@ export default class SpeechFlowNodeT2TAmazon extends SpeechFlowNode {
|
|
|
143
143
|
this.client = null
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
/*
|
|
146
|
+
/* shutdown stream */
|
|
147
147
|
if (this.stream !== null) {
|
|
148
|
-
this.stream
|
|
148
|
+
await util.destroyStream(this.stream)
|
|
149
149
|
this.stream = null
|
|
150
150
|
}
|
|
151
151
|
}
|
|
@@ -108,9 +108,9 @@ export default class SpeechFlowNodeT2TDeepL extends SpeechFlowNode {
|
|
|
108
108
|
|
|
109
109
|
/* close node */
|
|
110
110
|
async close () {
|
|
111
|
-
/*
|
|
111
|
+
/* shutdown stream */
|
|
112
112
|
if (this.stream !== null) {
|
|
113
|
-
this.stream
|
|
113
|
+
await util.destroyStream(this.stream)
|
|
114
114
|
this.stream = null
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -12,6 +12,7 @@ import wrapText from "wrap-text"
|
|
|
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 text-to-text formatting */
|
|
17
18
|
export default class SpeechFlowNodeT2TFormat extends SpeechFlowNode {
|
|
@@ -71,9 +72,9 @@ export default class SpeechFlowNodeT2TFormat extends SpeechFlowNode {
|
|
|
71
72
|
|
|
72
73
|
/* close node */
|
|
73
74
|
async close () {
|
|
74
|
-
/*
|
|
75
|
+
/* shutdown stream */
|
|
75
76
|
if (this.stream !== null) {
|
|
76
|
-
this.stream
|
|
77
|
+
await util.destroyStream(this.stream)
|
|
77
78
|
this.stream = null
|
|
78
79
|
}
|
|
79
80
|
}
|
|
@@ -118,9 +118,9 @@ export default class SpeechFlowNodeT2TGoogle extends SpeechFlowNode {
|
|
|
118
118
|
|
|
119
119
|
/* close node */
|
|
120
120
|
async close () {
|
|
121
|
-
/*
|
|
121
|
+
/* shutdown stream */
|
|
122
122
|
if (this.stream !== null) {
|
|
123
|
-
this.stream
|
|
123
|
+
await util.destroyStream(this.stream)
|
|
124
124
|
this.stream = null
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -26,6 +26,10 @@ export default class SpeechFlowNodeT2TModify extends SpeechFlowNode {
|
|
|
26
26
|
replace: { type: "string", val: "" }
|
|
27
27
|
})
|
|
28
28
|
|
|
29
|
+
/* sanity check parameters */
|
|
30
|
+
if (this.params.match === "")
|
|
31
|
+
throw new Error("match parameter cannot be empty")
|
|
32
|
+
|
|
29
33
|
/* declare node input/output format */
|
|
30
34
|
this.input = "text"
|
|
31
35
|
this.output = "text"
|
|
@@ -33,10 +37,6 @@ export default class SpeechFlowNodeT2TModify extends SpeechFlowNode {
|
|
|
33
37
|
|
|
34
38
|
/* open node */
|
|
35
39
|
async open () {
|
|
36
|
-
/* validate parameters */
|
|
37
|
-
if (this.params.match === "")
|
|
38
|
-
throw new Error("match parameter cannot be empty")
|
|
39
|
-
|
|
40
40
|
/* compile regex pattern */
|
|
41
41
|
const regex = util.run("compiling regex pattern",
|
|
42
42
|
() => new RegExp(this.params.match, "g"))
|
|
@@ -75,9 +75,9 @@ export default class SpeechFlowNodeT2TModify extends SpeechFlowNode {
|
|
|
75
75
|
|
|
76
76
|
/* close node */
|
|
77
77
|
async close () {
|
|
78
|
-
/*
|
|
78
|
+
/* shutdown stream */
|
|
79
79
|
if (this.stream !== null) {
|
|
80
|
-
this.stream
|
|
80
|
+
await util.destroyStream(this.stream)
|
|
81
81
|
this.stream = null
|
|
82
82
|
}
|
|
83
83
|
}
|
|
@@ -177,7 +177,7 @@ export default class SpeechFlowNodeT2TOllama extends SpeechFlowNode {
|
|
|
177
177
|
models = await this.ollama.list()
|
|
178
178
|
}
|
|
179
179
|
catch (err) {
|
|
180
|
-
throw new Error(`failed to connect to Ollama API at ${this.params.api}: ${err}
|
|
180
|
+
throw new Error(`failed to connect to Ollama API at ${this.params.api}: ${err}`, { cause: err })
|
|
181
181
|
}
|
|
182
182
|
const exists = models.models.some((m) => m.name === this.params.model)
|
|
183
183
|
if (!exists) {
|
|
@@ -266,9 +266,9 @@ export default class SpeechFlowNodeT2TOllama extends SpeechFlowNode {
|
|
|
266
266
|
|
|
267
267
|
/* close node */
|
|
268
268
|
async close () {
|
|
269
|
-
/*
|
|
269
|
+
/* shutdown stream */
|
|
270
270
|
if (this.stream !== null) {
|
|
271
|
-
this.stream
|
|
271
|
+
await util.destroyStream(this.stream)
|
|
272
272
|
this.stream = null
|
|
273
273
|
}
|
|
274
274
|
|
|
@@ -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
|
}
|
|
@@ -20,13 +20,10 @@ import HAPIWebSocket from "hapi-plugin-websocket"
|
|
|
20
20
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
21
21
|
import * as util from "./speechflow-util"
|
|
22
22
|
|
|
23
|
-
type
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
ctx: wsPeerCtx
|
|
28
|
-
ws: WebSocket
|
|
29
|
-
req: http.IncomingMessage
|
|
23
|
+
type WSPeerInfo = {
|
|
24
|
+
ctx: Record<string, any>
|
|
25
|
+
ws: WebSocket
|
|
26
|
+
req: http.IncomingMessage
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
/* SpeechFlow node for subtitle (text-to-text) "translations" */
|
|
@@ -160,7 +157,7 @@ export default class SpeechFlowNodeT2TSubtitle extends SpeechFlowNode {
|
|
|
160
157
|
}
|
|
161
158
|
else if (this.params.mode === "render") {
|
|
162
159
|
/* establish REST/WebSocket API */
|
|
163
|
-
const wsPeers = new Map<string,
|
|
160
|
+
const wsPeers = new Map<string, WSPeerInfo>()
|
|
164
161
|
this.hapi = new HAPI.Server({
|
|
165
162
|
address: this.params.addr,
|
|
166
163
|
port: this.params.port
|
|
@@ -205,19 +202,18 @@ export default class SpeechFlowNodeT2TSubtitle extends SpeechFlowNode {
|
|
|
205
202
|
plugins: {
|
|
206
203
|
websocket: {
|
|
207
204
|
autoping: 30 * 1000,
|
|
208
|
-
connect: (
|
|
209
|
-
const ctx: wsPeerCtx = args.ctx
|
|
210
|
-
const ws: WebSocket = args.ws
|
|
211
|
-
const req: http.IncomingMessage = args.req
|
|
205
|
+
connect: ({ ctx, ws, req }) => {
|
|
212
206
|
const peer = `${req.socket.remoteAddress}:${req.socket.remotePort}`
|
|
213
207
|
ctx.peer = peer
|
|
214
208
|
wsPeers.set(peer, { ctx, ws, req })
|
|
215
209
|
this.log("info", `HAPI: WebSocket: connect: peer ${peer}`)
|
|
216
210
|
},
|
|
217
|
-
disconnect: (
|
|
218
|
-
const ctx: wsPeerCtx = args.ctx
|
|
211
|
+
disconnect: ({ ctx, ws }) => {
|
|
219
212
|
const peer = ctx.peer
|
|
220
213
|
wsPeers.delete(peer)
|
|
214
|
+
ws.removeAllListeners()
|
|
215
|
+
if (ws.readyState === WebSocket.OPEN)
|
|
216
|
+
ws.close()
|
|
221
217
|
this.log("info", `HAPI: WebSocket: disconnect: peer ${peer}`)
|
|
222
218
|
}
|
|
223
219
|
}
|
|
@@ -261,9 +257,9 @@ export default class SpeechFlowNodeT2TSubtitle extends SpeechFlowNode {
|
|
|
261
257
|
|
|
262
258
|
/* close node */
|
|
263
259
|
async close () {
|
|
264
|
-
/*
|
|
260
|
+
/* shutdown stream */
|
|
265
261
|
if (this.stream !== null) {
|
|
266
|
-
this.stream
|
|
262
|
+
await util.destroyStream(this.stream)
|
|
267
263
|
this.stream = null
|
|
268
264
|
}
|
|
269
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"
|
|
@@ -115,7 +119,7 @@ export default class SpeechFlowNodeXIODevice extends SpeechFlowNode {
|
|
|
115
119
|
|
|
116
120
|
/* convert regular stream into object-mode stream */
|
|
117
121
|
const wrapper1 = util.createTransformStreamForWritableSide()
|
|
118
|
-
const wrapper2 = util.createTransformStreamForReadableSide("audio", () => this.timeZero)
|
|
122
|
+
const wrapper2 = util.createTransformStreamForReadableSide("audio", () => this.timeZero, highwaterMark)
|
|
119
123
|
this.stream = Stream.compose(wrapper1, this.stream, wrapper2)
|
|
120
124
|
}
|
|
121
125
|
|
|
@@ -136,7 +140,7 @@ export default class SpeechFlowNodeXIODevice extends SpeechFlowNode {
|
|
|
136
140
|
this.stream = this.io as unknown as Stream.Readable
|
|
137
141
|
|
|
138
142
|
/* convert regular stream into object-mode stream */
|
|
139
|
-
const wrapper = util.createTransformStreamForReadableSide("audio", () => this.timeZero)
|
|
143
|
+
const wrapper = util.createTransformStreamForReadableSide("audio", () => this.timeZero, highwaterMark)
|
|
140
144
|
this.stream = Stream.compose(this.stream, wrapper)
|
|
141
145
|
}
|
|
142
146
|
|
|
@@ -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
|
|
|
@@ -193,6 +194,7 @@ export default class SpeechFlowNodeXIODevice extends SpeechFlowNode {
|
|
|
193
194
|
/* pass-through PortAudio errors */
|
|
194
195
|
this.io!.on("error", (err) => {
|
|
195
196
|
this.emit("error", err)
|
|
197
|
+
this.stream?.emit("error", err)
|
|
196
198
|
})
|
|
197
199
|
|
|
198
200
|
/* start PortAudio */
|