speechflow 2.2.1 → 2.3.0

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.
Files changed (235) hide show
  1. package/{etc/claude.md → AGENTS.md} +8 -3
  2. package/CHANGELOG.md +70 -1
  3. package/README.md +28 -4
  4. package/etc/speechflow.yaml +3 -1
  5. package/etc/stx.conf +1 -1
  6. package/package.json +6 -6
  7. package/speechflow-cli/dst/speechflow-main-api.d.ts +2 -1
  8. package/speechflow-cli/dst/speechflow-main-api.js +57 -16
  9. package/speechflow-cli/dst/speechflow-main-api.js.map +1 -1
  10. package/speechflow-cli/dst/speechflow-main-cli.js +2 -2
  11. package/speechflow-cli/dst/speechflow-main-config.js +1 -1
  12. package/speechflow-cli/dst/speechflow-main-graph.js +55 -21
  13. package/speechflow-cli/dst/speechflow-main-graph.js.map +1 -1
  14. package/speechflow-cli/dst/speechflow-main-nodes.js +1 -1
  15. package/speechflow-cli/dst/speechflow-main-status.js +6 -3
  16. package/speechflow-cli/dst/speechflow-main-status.js.map +1 -1
  17. package/speechflow-cli/dst/speechflow-main.js +1 -1
  18. package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js +7 -10
  19. package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js.map +1 -1
  20. package/speechflow-cli/dst/speechflow-node-a2a-compressor.js +8 -6
  21. package/speechflow-cli/dst/speechflow-node-a2a-compressor.js.map +1 -1
  22. package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js +9 -5
  23. package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js.map +1 -1
  24. package/speechflow-cli/dst/speechflow-node-a2a-expander.js +6 -5
  25. package/speechflow-cli/dst/speechflow-node-a2a-expander.js.map +1 -1
  26. package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js +2 -2
  27. package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js.map +1 -1
  28. package/speechflow-cli/dst/speechflow-node-a2a-filler.js +2 -4
  29. package/speechflow-cli/dst/speechflow-node-a2a-filler.js.map +1 -1
  30. package/speechflow-cli/dst/speechflow-node-a2a-gain.js +1 -1
  31. package/speechflow-cli/dst/speechflow-node-a2a-gender.js +20 -12
  32. package/speechflow-cli/dst/speechflow-node-a2a-gender.js.map +1 -1
  33. package/speechflow-cli/dst/speechflow-node-a2a-gtcrn-wt.js +1 -1
  34. package/speechflow-cli/dst/speechflow-node-a2a-gtcrn.js +33 -11
  35. package/speechflow-cli/dst/speechflow-node-a2a-gtcrn.js.map +1 -1
  36. package/speechflow-cli/dst/speechflow-node-a2a-meter.js +1 -1
  37. package/speechflow-cli/dst/speechflow-node-a2a-mute.js +1 -1
  38. package/speechflow-cli/dst/speechflow-node-a2a-pitch.js +4 -3
  39. package/speechflow-cli/dst/speechflow-node-a2a-pitch.js.map +1 -1
  40. package/speechflow-cli/dst/speechflow-node-a2a-rnnoise-wt.js +2 -2
  41. package/speechflow-cli/dst/speechflow-node-a2a-rnnoise-wt.js.map +1 -1
  42. package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js +19 -11
  43. package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js.map +1 -1
  44. package/speechflow-cli/dst/speechflow-node-a2a-speex.js +8 -8
  45. package/speechflow-cli/dst/speechflow-node-a2a-speex.js.map +1 -1
  46. package/speechflow-cli/dst/speechflow-node-a2a-vad.js +33 -29
  47. package/speechflow-cli/dst/speechflow-node-a2a-vad.js.map +1 -1
  48. package/speechflow-cli/dst/speechflow-node-a2a-wav.js +6 -5
  49. package/speechflow-cli/dst/speechflow-node-a2a-wav.js.map +1 -1
  50. package/speechflow-cli/dst/speechflow-node-a2t-amazon.d.ts +2 -1
  51. package/speechflow-cli/dst/speechflow-node-a2t-amazon.js +34 -20
  52. package/speechflow-cli/dst/speechflow-node-a2t-amazon.js.map +1 -1
  53. package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js +13 -5
  54. package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js.map +1 -1
  55. package/speechflow-cli/dst/speechflow-node-a2t-google.js +3 -2
  56. package/speechflow-cli/dst/speechflow-node-a2t-google.js.map +1 -1
  57. package/speechflow-cli/dst/speechflow-node-a2t-openai.js +33 -27
  58. package/speechflow-cli/dst/speechflow-node-a2t-openai.js.map +1 -1
  59. package/speechflow-cli/dst/speechflow-node-t2a-amazon.js +16 -5
  60. package/speechflow-cli/dst/speechflow-node-t2a-amazon.js.map +1 -1
  61. package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js +17 -5
  62. package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js.map +1 -1
  63. package/speechflow-cli/dst/speechflow-node-t2a-google.js +17 -5
  64. package/speechflow-cli/dst/speechflow-node-t2a-google.js.map +1 -1
  65. package/speechflow-cli/dst/speechflow-node-t2a-kitten.d.ts +15 -0
  66. package/speechflow-cli/dst/speechflow-node-t2a-kitten.js +194 -0
  67. package/speechflow-cli/dst/speechflow-node-t2a-kitten.js.map +1 -0
  68. package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js +21 -9
  69. package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js.map +1 -1
  70. package/speechflow-cli/dst/speechflow-node-t2a-openai.js +17 -5
  71. package/speechflow-cli/dst/speechflow-node-t2a-openai.js.map +1 -1
  72. package/speechflow-cli/dst/speechflow-node-t2a-supertonic.js +21 -7
  73. package/speechflow-cli/dst/speechflow-node-t2a-supertonic.js.map +1 -1
  74. package/speechflow-cli/dst/speechflow-node-t2t-amazon.js +1 -1
  75. package/speechflow-cli/dst/speechflow-node-t2t-deepl.js +1 -1
  76. package/speechflow-cli/dst/speechflow-node-t2t-format.js +1 -1
  77. package/speechflow-cli/dst/speechflow-node-t2t-google.js +4 -2
  78. package/speechflow-cli/dst/speechflow-node-t2t-google.js.map +1 -1
  79. package/speechflow-cli/dst/speechflow-node-t2t-modify.js +1 -1
  80. package/speechflow-cli/dst/speechflow-node-t2t-opus.js +1 -1
  81. package/speechflow-cli/dst/speechflow-node-t2t-profanity.js +1 -1
  82. package/speechflow-cli/dst/speechflow-node-t2t-punctuation.js +1 -1
  83. package/speechflow-cli/dst/speechflow-node-t2t-sentence.js +1 -1
  84. package/speechflow-cli/dst/speechflow-node-t2t-spellcheck.js +1 -1
  85. package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js +34 -14
  86. package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js.map +1 -1
  87. package/speechflow-cli/dst/speechflow-node-t2t-summary.js +3 -3
  88. package/speechflow-cli/dst/speechflow-node-t2t-summary.js.map +1 -1
  89. package/speechflow-cli/dst/speechflow-node-t2t-translate.js +1 -1
  90. package/speechflow-cli/dst/speechflow-node-x2x-filter.js +3 -2
  91. package/speechflow-cli/dst/speechflow-node-x2x-filter.js.map +1 -1
  92. package/speechflow-cli/dst/speechflow-node-x2x-trace.js +1 -1
  93. package/speechflow-cli/dst/speechflow-node-xio-device.js +18 -7
  94. package/speechflow-cli/dst/speechflow-node-xio-device.js.map +1 -1
  95. package/speechflow-cli/dst/speechflow-node-xio-exec.js +23 -11
  96. package/speechflow-cli/dst/speechflow-node-xio-exec.js.map +1 -1
  97. package/speechflow-cli/dst/speechflow-node-xio-file.js +13 -7
  98. package/speechflow-cli/dst/speechflow-node-xio-file.js.map +1 -1
  99. package/speechflow-cli/dst/speechflow-node-xio-mqtt.js +25 -12
  100. package/speechflow-cli/dst/speechflow-node-xio-mqtt.js.map +1 -1
  101. package/speechflow-cli/dst/speechflow-node-xio-vban.js +32 -20
  102. package/speechflow-cli/dst/speechflow-node-xio-vban.js.map +1 -1
  103. package/speechflow-cli/dst/speechflow-node-xio-webrtc.js +78 -62
  104. package/speechflow-cli/dst/speechflow-node-xio-webrtc.js.map +1 -1
  105. package/speechflow-cli/dst/speechflow-node-xio-websocket.d.ts +1 -0
  106. package/speechflow-cli/dst/speechflow-node-xio-websocket.js +63 -18
  107. package/speechflow-cli/dst/speechflow-node-xio-websocket.js.map +1 -1
  108. package/speechflow-cli/dst/speechflow-node.js +5 -7
  109. package/speechflow-cli/dst/speechflow-node.js.map +1 -1
  110. package/speechflow-cli/dst/speechflow-util-audio-wt.js +31 -5
  111. package/speechflow-cli/dst/speechflow-util-audio-wt.js.map +1 -1
  112. package/speechflow-cli/dst/speechflow-util-audio.d.ts +1 -1
  113. package/speechflow-cli/dst/speechflow-util-audio.js +25 -14
  114. package/speechflow-cli/dst/speechflow-util-audio.js.map +1 -1
  115. package/speechflow-cli/dst/speechflow-util-error.d.ts +1 -1
  116. package/speechflow-cli/dst/speechflow-util-error.js +2 -2
  117. package/speechflow-cli/dst/speechflow-util-error.js.map +1 -1
  118. package/speechflow-cli/dst/speechflow-util-llm.js +1 -1
  119. package/speechflow-cli/dst/speechflow-util-misc.d.ts +3 -2
  120. package/speechflow-cli/dst/speechflow-util-misc.js +63 -6
  121. package/speechflow-cli/dst/speechflow-util-misc.js.map +1 -1
  122. package/speechflow-cli/dst/speechflow-util-queue.d.ts +5 -17
  123. package/speechflow-cli/dst/speechflow-util-queue.js +57 -78
  124. package/speechflow-cli/dst/speechflow-util-queue.js.map +1 -1
  125. package/speechflow-cli/dst/speechflow-util-stream.d.ts +1 -1
  126. package/speechflow-cli/dst/speechflow-util-stream.js +35 -8
  127. package/speechflow-cli/dst/speechflow-util-stream.js.map +1 -1
  128. package/speechflow-cli/dst/speechflow-util.js +1 -1
  129. package/speechflow-cli/dst/speechflow.d.ts +1 -1
  130. package/speechflow-cli/dst/speechflow.js +1 -1
  131. package/speechflow-cli/etc/eslint.mjs +1 -1
  132. package/speechflow-cli/etc/oxlint.jsonc +2 -1
  133. package/speechflow-cli/etc/stx.conf +8 -2
  134. package/speechflow-cli/package.d/@ericedouard+vad-node-realtime+0.2.0.patch +2 -1
  135. package/speechflow-cli/package.d/@typescript-eslint+typescript-estree+8.57.2.patch +12 -0
  136. package/speechflow-cli/package.d/kitten-tts-js+0.1.2.patch +24 -0
  137. package/speechflow-cli/package.d/speex-resampler+3.0.1.patch +56 -0
  138. package/speechflow-cli/package.json +40 -30
  139. package/speechflow-cli/src/lib.d.ts +19 -1
  140. package/speechflow-cli/src/speechflow-main-api.ts +64 -19
  141. package/speechflow-cli/src/speechflow-main-cli.ts +2 -2
  142. package/speechflow-cli/src/speechflow-main-config.ts +1 -1
  143. package/speechflow-cli/src/speechflow-main-graph.ts +56 -22
  144. package/speechflow-cli/src/speechflow-main-nodes.ts +1 -1
  145. package/speechflow-cli/src/speechflow-main-status.ts +6 -3
  146. package/speechflow-cli/src/speechflow-main.ts +1 -1
  147. package/speechflow-cli/src/speechflow-node-a2a-compressor-wt.ts +7 -11
  148. package/speechflow-cli/src/speechflow-node-a2a-compressor.ts +8 -6
  149. package/speechflow-cli/src/speechflow-node-a2a-expander-wt.ts +10 -5
  150. package/speechflow-cli/src/speechflow-node-a2a-expander.ts +6 -5
  151. package/speechflow-cli/src/speechflow-node-a2a-ffmpeg.ts +3 -2
  152. package/speechflow-cli/src/speechflow-node-a2a-filler.ts +2 -4
  153. package/speechflow-cli/src/speechflow-node-a2a-gain.ts +1 -1
  154. package/speechflow-cli/src/speechflow-node-a2a-gender.ts +20 -13
  155. package/speechflow-cli/src/speechflow-node-a2a-gtcrn-wt.ts +1 -1
  156. package/speechflow-cli/src/speechflow-node-a2a-gtcrn.ts +43 -16
  157. package/speechflow-cli/src/speechflow-node-a2a-meter.ts +1 -1
  158. package/speechflow-cli/src/speechflow-node-a2a-mute.ts +1 -1
  159. package/speechflow-cli/src/speechflow-node-a2a-pitch.ts +4 -3
  160. package/speechflow-cli/src/speechflow-node-a2a-rnnoise-wt.ts +2 -2
  161. package/speechflow-cli/src/speechflow-node-a2a-rnnoise.ts +24 -12
  162. package/speechflow-cli/src/speechflow-node-a2a-speex.ts +10 -9
  163. package/speechflow-cli/src/speechflow-node-a2a-vad.ts +38 -31
  164. package/speechflow-cli/src/speechflow-node-a2a-wav.ts +6 -5
  165. package/speechflow-cli/src/speechflow-node-a2t-amazon.ts +35 -22
  166. package/speechflow-cli/src/speechflow-node-a2t-deepgram.ts +17 -6
  167. package/speechflow-cli/src/speechflow-node-a2t-google.ts +5 -4
  168. package/speechflow-cli/src/speechflow-node-a2t-openai.ts +39 -31
  169. package/speechflow-cli/src/speechflow-node-t2a-amazon.ts +16 -5
  170. package/speechflow-cli/src/speechflow-node-t2a-elevenlabs.ts +17 -5
  171. package/speechflow-cli/src/speechflow-node-t2a-google.ts +17 -5
  172. package/speechflow-cli/src/speechflow-node-t2a-kitten.ts +178 -0
  173. package/speechflow-cli/src/speechflow-node-t2a-kokoro.ts +21 -9
  174. package/speechflow-cli/src/speechflow-node-t2a-openai.ts +17 -5
  175. package/speechflow-cli/src/speechflow-node-t2a-supertonic.ts +21 -7
  176. package/speechflow-cli/src/speechflow-node-t2t-amazon.ts +1 -1
  177. package/speechflow-cli/src/speechflow-node-t2t-deepl.ts +1 -1
  178. package/speechflow-cli/src/speechflow-node-t2t-format.ts +1 -1
  179. package/speechflow-cli/src/speechflow-node-t2t-google.ts +4 -2
  180. package/speechflow-cli/src/speechflow-node-t2t-modify.ts +1 -1
  181. package/speechflow-cli/src/speechflow-node-t2t-opus.ts +1 -1
  182. package/speechflow-cli/src/speechflow-node-t2t-profanity.ts +1 -1
  183. package/speechflow-cli/src/speechflow-node-t2t-punctuation.ts +1 -1
  184. package/speechflow-cli/src/speechflow-node-t2t-sentence.ts +1 -1
  185. package/speechflow-cli/src/speechflow-node-t2t-spellcheck.ts +1 -1
  186. package/speechflow-cli/src/speechflow-node-t2t-subtitle.ts +39 -15
  187. package/speechflow-cli/src/speechflow-node-t2t-summary.ts +3 -3
  188. package/speechflow-cli/src/speechflow-node-t2t-translate.ts +1 -1
  189. package/speechflow-cli/src/speechflow-node-x2x-filter.ts +4 -3
  190. package/speechflow-cli/src/speechflow-node-x2x-trace.ts +1 -1
  191. package/speechflow-cli/src/speechflow-node-xio-device.ts +21 -7
  192. package/speechflow-cli/src/speechflow-node-xio-exec.ts +25 -11
  193. package/speechflow-cli/src/speechflow-node-xio-file.ts +15 -7
  194. package/speechflow-cli/src/speechflow-node-xio-mqtt.ts +28 -15
  195. package/speechflow-cli/src/speechflow-node-xio-vban.ts +35 -22
  196. package/speechflow-cli/src/speechflow-node-xio-webrtc.ts +85 -69
  197. package/speechflow-cli/src/speechflow-node-xio-websocket.ts +67 -20
  198. package/speechflow-cli/src/speechflow-node.ts +7 -8
  199. package/speechflow-cli/src/speechflow-util-audio-wt.ts +46 -7
  200. package/speechflow-cli/src/speechflow-util-audio.ts +27 -15
  201. package/speechflow-cli/src/speechflow-util-error.ts +3 -3
  202. package/speechflow-cli/src/speechflow-util-llm.ts +1 -1
  203. package/speechflow-cli/src/speechflow-util-misc.ts +63 -6
  204. package/speechflow-cli/src/speechflow-util-queue.ts +60 -81
  205. package/speechflow-cli/src/speechflow-util-stream.ts +40 -8
  206. package/speechflow-cli/src/speechflow-util.ts +1 -1
  207. package/speechflow-cli/src/speechflow.ts +1 -1
  208. package/speechflow-ui-db/dst/index.html +1 -1
  209. package/speechflow-ui-db/dst/index.js +15 -15
  210. package/speechflow-ui-db/etc/eslint.mjs +1 -1
  211. package/speechflow-ui-db/etc/oxlint.jsonc +1 -1
  212. package/speechflow-ui-db/etc/stx.conf +1 -1
  213. package/speechflow-ui-db/etc/stylelint.js +1 -1
  214. package/speechflow-ui-db/etc/stylelint.yaml +1 -1
  215. package/speechflow-ui-db/etc/vite-client.mts +1 -1
  216. package/speechflow-ui-db/package.d/@typescript-eslint+typescript-estree+8.57.2.patch +12 -0
  217. package/speechflow-ui-db/package.json +22 -16
  218. package/speechflow-ui-db/src/app.styl +1 -1
  219. package/speechflow-ui-db/src/app.vue +1 -1
  220. package/speechflow-ui-db/src/index.html +1 -1
  221. package/speechflow-ui-db/src/index.ts +1 -1
  222. package/speechflow-ui-st/dst/index.html +1 -1
  223. package/speechflow-ui-st/dst/index.js +31 -31
  224. package/speechflow-ui-st/etc/eslint.mjs +1 -1
  225. package/speechflow-ui-st/etc/oxlint.jsonc +1 -1
  226. package/speechflow-ui-st/etc/stx.conf +1 -1
  227. package/speechflow-ui-st/etc/stylelint.js +1 -1
  228. package/speechflow-ui-st/etc/stylelint.yaml +1 -1
  229. package/speechflow-ui-st/etc/vite-client.mts +1 -1
  230. package/speechflow-ui-st/package.d/@typescript-eslint+typescript-estree+8.57.2.patch +12 -0
  231. package/speechflow-ui-st/package.json +23 -17
  232. package/speechflow-ui-st/src/app.styl +1 -1
  233. package/speechflow-ui-st/src/app.vue +1 -1
  234. package/speechflow-ui-st/src/index.html +1 -1
  235. package/speechflow-ui-st/src/index.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  ** SpeechFlow - Speech Processing Flow Graph
3
- ** Copyright (c) 2024-2025 Dr. Ralf S. Engelschall <rse@engelschall.com>
3
+ ** Copyright (c) 2024-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
4
4
  ** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
5
5
  */
6
6
 
@@ -26,70 +26,6 @@ export function importObject<T> (name: string, arg: object | string, validator:
26
26
  return result as T
27
27
  }
28
28
 
29
- /* helper class for single item queue */
30
- export class SingleQueue<T> extends EventEmitter {
31
- private queue = new Array<T>()
32
- write (item: T) {
33
- this.queue.unshift(item)
34
- this.emit("dequeue")
35
- }
36
- read () {
37
- return new Promise<T>((resolve) => {
38
- const tryToConsume = () => {
39
- const item = this.queue.pop()
40
- if (item !== undefined)
41
- resolve(item)
42
- else
43
- this.once("dequeue", tryToConsume)
44
- }
45
- tryToConsume()
46
- })
47
- }
48
- drain () {
49
- const items = this.queue
50
- this.queue = new Array<T>()
51
- return items
52
- }
53
- }
54
-
55
- /* helper class for double-item queue */
56
- export class DoubleQueue<T0, T1> extends EventEmitter {
57
- private queue0 = new Array<T0>()
58
- private queue1 = new Array<T1>()
59
- private notify () {
60
- if (this.queue0.length > 0 && this.queue1.length > 0)
61
- this.emit("dequeue")
62
- }
63
- write0 (item: T0) {
64
- this.queue0.unshift(item)
65
- this.notify()
66
- }
67
- write1 (item: T1) {
68
- this.queue1.unshift(item)
69
- this.notify()
70
- }
71
- read () {
72
- return new Promise<[ T0, T1 ]>((resolve) => {
73
- const consume = (): [ T0, T1 ] | undefined => {
74
- if (this.queue0.length > 0 && this.queue1.length > 0) {
75
- const item0 = this.queue0.pop() as T0
76
- const item1 = this.queue1.pop() as T1
77
- return [ item0, item1 ]
78
- }
79
- return undefined
80
- }
81
- const tryToConsume = () => {
82
- const items = consume()
83
- if (items !== undefined)
84
- resolve(items)
85
- else
86
- this.once("dequeue", tryToConsume)
87
- }
88
- tryToConsume()
89
- })
90
- }
91
- }
92
-
93
29
  /* queue element */
94
30
  export type QueueElement = { type: string }
95
31
 
@@ -126,7 +62,7 @@ export class QueuePointer<T extends QueueElement> extends EventEmitter {
126
62
  position (index?: number): number {
127
63
  if (index !== undefined) {
128
64
  this.index = Math.max(0, Math.min(index, this.queue.elements.length))
129
- this.notify("position", this.index)
65
+ this.notify("position", { start: this.index })
130
66
  }
131
67
  return this.index
132
68
  }
@@ -139,35 +75,53 @@ export class QueuePointer<T extends QueueElement> extends EventEmitter {
139
75
  if (this.index !== indexOld)
140
76
  this.notify("position", { start: this.index })
141
77
  }
142
- walkForwardUntil (type: T["type"]) {
78
+ walkForwardUntil (type: T["type"]): boolean {
143
79
  while (this.index < this.queue.elements.length
144
80
  && this.queue.elements[this.index].type !== type)
145
81
  this.index++
146
82
  this.notify("position", { start: this.index })
83
+ return this.index < this.queue.elements.length
84
+ && this.queue.elements[this.index].type === type
147
85
  }
148
- walkBackwardUntil (type: T["type"]) {
149
- while (this.index > 0
150
- && this.queue.elements[this.index].type !== type)
86
+ walkBackwardUntil (type: T["type"]): boolean {
87
+ if (this.index === this.queue.elements.length && this.index > 0)
151
88
  this.index--
89
+ while (this.index < this.queue.elements.length
90
+ && this.queue.elements[this.index].type !== type) {
91
+ if (this.index === 0)
92
+ break
93
+ this.index--
94
+ }
152
95
  this.notify("position", { start: this.index })
96
+ return this.index < this.queue.elements.length
97
+ && this.queue.elements[this.index].type === type
153
98
  }
154
99
 
155
100
  /* search operations */
156
- searchForward (type: T["type"]) {
101
+ searchForward (type: T["type"]): number {
157
102
  let position = this.index
158
103
  while (position < this.queue.elements.length
159
104
  && this.queue.elements[position].type !== type)
160
105
  position++
161
- this.notify("search", { start: this.index, end: position })
162
- return position
106
+ const found = position < this.queue.elements.length
107
+ && this.queue.elements[position].type === type
108
+ this.notify("search", { start: this.index, end: found ? position : this.index })
109
+ return found ? position : -1
163
110
  }
164
- searchBackward (type: T["type"]) {
111
+ searchBackward (type: T["type"]): number {
165
112
  let position = this.index
166
- while (position > 0
167
- && this.queue.elements[position].type !== type)
113
+ if (position === this.queue.elements.length && position > 0)
168
114
  position--
169
- this.notify("search", { start: position, end: this.index })
170
- return position
115
+ while (position < this.queue.elements.length
116
+ && this.queue.elements[position].type !== type) {
117
+ if (position === 0)
118
+ break
119
+ position--
120
+ }
121
+ const found = position < this.queue.elements.length
122
+ && this.queue.elements[position].type === type
123
+ this.notify("search", { start: found ? position : this.index, end: this.index })
124
+ return found ? position : -1
171
125
  }
172
126
 
173
127
  /* reading operations */
@@ -269,6 +223,7 @@ export class Queue<T extends QueueElement> extends EventEmitter {
269
223
  for (const pointer of this.pointers.values())
270
224
  pointer.position(pointer.position() - min)
271
225
 
226
+ /* notify (start/end refer to pre-splice indices) */
272
227
  this.notify("write", { start: 0, end: min, op: "trim" })
273
228
  }
274
229
  }
@@ -301,9 +256,12 @@ export class TimeStore<T> extends EventEmitter {
301
256
 
302
257
  /* asynchronous queue */
303
258
  export class AsyncQueue<T> {
304
- private queue: Array<T> = []
259
+ private queue = new Array<T>()
305
260
  private resolvers: { resolve: (v: T) => void, reject: (err: Error) => void }[] = []
261
+ public destroyed = false
306
262
  write (v: T) {
263
+ if (this.destroyed)
264
+ return
307
265
  const resolver = this.resolvers.shift()
308
266
  if (resolver)
309
267
  resolver.resolve(v)
@@ -319,7 +277,13 @@ export class AsyncQueue<T> {
319
277
  empty () {
320
278
  return this.queue.length === 0
321
279
  }
280
+ drain () {
281
+ const items = this.queue
282
+ this.queue = new Array<T>()
283
+ return items
284
+ }
322
285
  destroy () {
286
+ this.destroyed = true
323
287
  for (const resolver of this.resolvers)
324
288
  resolver.reject(new Error("AsyncQueue destroyed"))
325
289
  this.resolvers = []
@@ -359,7 +323,22 @@ export class PromiseSet<T> {
359
323
  this.promises.delete(promise)
360
324
  }).catch(() => {})
361
325
  }
362
- async awaitAll () {
363
- await Promise.all(this.promises)
326
+ async awaitAll (timeout = 0) {
327
+ const deadline = timeout > 0 ? Date.now() + timeout : 0
328
+ while (this.promises.size > 0) {
329
+ const snapshot = [ ...this.promises ]
330
+ const remaining = deadline > 0 ? deadline - Date.now() : 0
331
+ if (deadline > 0 && remaining <= 0)
332
+ break
333
+ if (deadline > 0)
334
+ await Promise.race([
335
+ Promise.all(snapshot),
336
+ new Promise((resolve) => { setTimeout(resolve, remaining) })
337
+ ])
338
+ else
339
+ await Promise.all(snapshot)
340
+ if (deadline > 0 && Date.now() >= deadline)
341
+ break
342
+ }
364
343
  }
365
344
  }
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  ** SpeechFlow - Speech Processing Flow Graph
3
- ** Copyright (c) 2024-2025 Dr. Ralf S. Engelschall <rse@engelschall.com>
3
+ ** Copyright (c) 2024-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
4
4
  ** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
5
5
  */
6
6
 
@@ -59,7 +59,10 @@ export function ensureStreamChunk (type: "audio" | "text", chunk: SpeechFlowChun
59
59
  export function createTransformStreamForReadableSide (
60
60
  type: "text" | "audio",
61
61
  getTimeZero: () => DateTime,
62
- writableHighWaterMark?: number
62
+ writableHighWaterMark?: number,
63
+ sampleRate?: number,
64
+ bitDepth?: number,
65
+ channels?: number
63
66
  ) {
64
67
  return new Stream.Transform({
65
68
  writableObjectMode: false,
@@ -72,7 +75,8 @@ export function createTransformStreamForReadableSide (
72
75
  const start = DateTime.now().diff(timeZero)
73
76
  let end = start
74
77
  if (type === "audio") {
75
- const duration = util.audioBufferDuration(chunk as Buffer)
78
+ const duration = util.audioBufferDuration(chunk as Buffer,
79
+ sampleRate, bitDepth, channels)
76
80
  end = start.plus(duration * 1000)
77
81
  }
78
82
  const payload = ensureStreamChunk(type, chunk) as Buffer | string
@@ -185,9 +189,33 @@ export class StreamWrapper extends Stream.Transform {
185
189
  return
186
190
  }
187
191
  try {
188
- if (typeof this.foreignStream.end === "function")
192
+ if (typeof this.foreignStream.end === "function") {
189
193
  this.foreignStream.end()
190
- callback()
194
+
195
+ /* wait for the foreign stream to finish flushing
196
+ before signaling completion of this Transform stream */
197
+ const ac = new AbortController()
198
+ Promise.race([
199
+ new Promise<void>((resolve, reject) => {
200
+ if (typeof this.foreignStream.once === "function") {
201
+ this.foreignStream.once("finish", () => { resolve() })
202
+ this.foreignStream.once("error", (err: Error) => { reject(err) })
203
+ }
204
+ else
205
+ resolve()
206
+ }),
207
+ util.timeout(5000, "foreign stream flush timeout", ac.signal)
208
+ ]).finally(() => {
209
+ ac.abort()
210
+ }).then(() => {
211
+ callback()
212
+ }).catch(() => {
213
+ /* ignore timeout -- stream will be destroyed anyway */
214
+ callback()
215
+ })
216
+ }
217
+ else
218
+ callback()
191
219
  }
192
220
  catch (err: unknown) {
193
221
  callback(util.ensureError(err))
@@ -211,15 +239,19 @@ export async function destroyStream (
211
239
  if (( stream instanceof Stream.Duplex
212
240
  || stream instanceof Stream.Transform
213
241
  || stream instanceof Stream.Writable )
214
- && (!stream.writableEnded && !stream.destroyed))
242
+ && (!stream.writableEnded && !stream.destroyed)) {
243
+ const ac = new AbortController()
215
244
  await Promise.race([
216
245
  new Promise<void>((resolve) => {
217
246
  stream.end(() => { resolve() })
218
247
  }),
219
- util.timeout(5000, "stream end timeout")
220
- ]).catch(() => {
248
+ util.timeout(5000, "stream end timeout", ac.signal)
249
+ ]).finally(() => {
250
+ ac.abort()
251
+ }).catch(() => {
221
252
  /* ignore timeout -- stream will be destroyed anyway */
222
253
  })
254
+ }
223
255
 
224
256
  /* destroy the stream */
225
257
  stream.destroy()
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  ** SpeechFlow - Speech Processing Flow Graph
3
- ** Copyright (c) 2024-2025 Dr. Ralf S. Engelschall <rse@engelschall.com>
3
+ ** Copyright (c) 2024-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
4
4
  ** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
5
5
  */
6
6
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /*!
3
3
  ** SpeechFlow - Speech Processing Flow Graph
4
- ** Copyright (c) 2024-2025 Dr. Ralf S. Engelschall <rse@engelschall.com>
4
+ ** Copyright (c) 2024-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
5
5
  ** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
6
6
  */
7
7
 
@@ -2,7 +2,7 @@
2
2
  <!--
3
3
  **
4
4
  ** SpeechFlow - Speech Processing Flow Graph
5
- ** Copyright (c) 2024-2025 Dr. Ralf S. Engelschall <rse@engelschall.com>
5
+ ** Copyright (c) 2024-2026 Dr. Ralf S. Engelschall <rse@engelschall.com>
6
6
  ** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
7
7
  **
8
8
  -->