speechflow 1.5.1 → 1.6.1

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 (232) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +192 -171
  3. package/etc/claude.md +83 -46
  4. package/etc/speechflow.yaml +84 -84
  5. package/package.json +3 -3
  6. package/speechflow-cli/dst/speechflow-main-api.d.ts +12 -0
  7. package/speechflow-cli/dst/speechflow-main-api.js +319 -0
  8. package/speechflow-cli/dst/speechflow-main-api.js.map +1 -0
  9. package/speechflow-cli/dst/speechflow-main-cli.d.ts +28 -0
  10. package/speechflow-cli/dst/speechflow-main-cli.js +271 -0
  11. package/speechflow-cli/dst/speechflow-main-cli.js.map +1 -0
  12. package/speechflow-cli/dst/speechflow-main-config.d.ts +9 -0
  13. package/speechflow-cli/dst/speechflow-main-config.js +27 -0
  14. package/speechflow-cli/dst/speechflow-main-config.js.map +1 -0
  15. package/speechflow-cli/dst/speechflow-main-graph.d.ts +34 -0
  16. package/speechflow-cli/dst/speechflow-main-graph.js +367 -0
  17. package/speechflow-cli/dst/speechflow-main-graph.js.map +1 -0
  18. package/speechflow-cli/dst/speechflow-main-nodes.d.ts +10 -0
  19. package/speechflow-cli/dst/speechflow-main-nodes.js +60 -0
  20. package/speechflow-cli/dst/speechflow-main-nodes.js.map +1 -0
  21. package/speechflow-cli/dst/speechflow-main-status.d.ts +11 -0
  22. package/speechflow-cli/dst/speechflow-main-status.js +60 -0
  23. package/speechflow-cli/dst/speechflow-main-status.js.map +1 -0
  24. package/speechflow-cli/dst/speechflow-main.d.ts +7 -0
  25. package/speechflow-cli/dst/speechflow-main.js +127 -0
  26. package/speechflow-cli/dst/speechflow-main.js.map +1 -0
  27. package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js +4 -4
  28. package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js.map +1 -1
  29. package/speechflow-cli/dst/speechflow-node-a2a-compressor.d.ts +1 -1
  30. package/speechflow-cli/dst/speechflow-node-a2a-compressor.js +8 -9
  31. package/speechflow-cli/dst/speechflow-node-a2a-compressor.js.map +1 -1
  32. package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js +5 -5
  33. package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js.map +1 -1
  34. package/speechflow-cli/dst/speechflow-node-a2a-expander.d.ts +1 -1
  35. package/speechflow-cli/dst/speechflow-node-a2a-expander.js +8 -9
  36. package/speechflow-cli/dst/speechflow-node-a2a-expander.js.map +1 -1
  37. package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.d.ts +1 -1
  38. package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js +8 -8
  39. package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js.map +1 -1
  40. package/speechflow-cli/dst/speechflow-node-a2a-filler.d.ts +1 -1
  41. package/speechflow-cli/dst/speechflow-node-a2a-filler.js +6 -6
  42. package/speechflow-cli/dst/speechflow-node-a2a-filler.js.map +1 -1
  43. package/speechflow-cli/dst/speechflow-node-a2a-gain.d.ts +1 -1
  44. package/speechflow-cli/dst/speechflow-node-a2a-gain.js +5 -5
  45. package/speechflow-cli/dst/speechflow-node-a2a-gain.js.map +1 -1
  46. package/speechflow-cli/dst/speechflow-node-a2a-gender.d.ts +1 -1
  47. package/speechflow-cli/dst/speechflow-node-a2a-gender.js +7 -7
  48. package/speechflow-cli/dst/speechflow-node-a2a-gender.js.map +1 -1
  49. package/speechflow-cli/dst/speechflow-node-a2a-meter.d.ts +1 -1
  50. package/speechflow-cli/dst/speechflow-node-a2a-meter.js +5 -5
  51. package/speechflow-cli/dst/speechflow-node-a2a-meter.js.map +1 -1
  52. package/speechflow-cli/dst/speechflow-node-a2a-mute.d.ts +1 -1
  53. package/speechflow-cli/dst/speechflow-node-a2a-mute.js +3 -3
  54. package/speechflow-cli/dst/speechflow-node-a2a-mute.js.map +1 -1
  55. package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.d.ts +1 -1
  56. package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js +7 -7
  57. package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js.map +1 -1
  58. package/speechflow-cli/dst/speechflow-node-a2a-speex.d.ts +1 -1
  59. package/speechflow-cli/dst/speechflow-node-a2a-speex.js +7 -7
  60. package/speechflow-cli/dst/speechflow-node-a2a-speex.js.map +1 -1
  61. package/speechflow-cli/dst/speechflow-node-a2a-vad.d.ts +1 -1
  62. package/speechflow-cli/dst/speechflow-node-a2a-vad.js +7 -7
  63. package/speechflow-cli/dst/speechflow-node-a2a-vad.js.map +1 -1
  64. package/speechflow-cli/dst/speechflow-node-a2a-wav.d.ts +1 -1
  65. package/speechflow-cli/dst/speechflow-node-a2a-wav.js +3 -3
  66. package/speechflow-cli/dst/speechflow-node-a2a-wav.js.map +1 -1
  67. package/speechflow-cli/dst/{speechflow-node-a2t-awstranscribe.d.ts → speechflow-node-a2t-amazon.d.ts} +1 -1
  68. package/speechflow-cli/dst/{speechflow-node-a2t-awstranscribe.js → speechflow-node-a2t-amazon.js} +11 -11
  69. package/speechflow-cli/dst/speechflow-node-a2t-amazon.js.map +1 -0
  70. package/speechflow-cli/dst/speechflow-node-a2t-deepgram.d.ts +1 -1
  71. package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js +7 -7
  72. package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js.map +1 -1
  73. package/speechflow-cli/dst/{speechflow-node-a2t-openaitranscribe.d.ts → speechflow-node-a2t-openai.d.ts} +1 -1
  74. package/speechflow-cli/dst/{speechflow-node-a2t-openaitranscribe.js → speechflow-node-a2t-openai.js} +11 -11
  75. package/speechflow-cli/dst/speechflow-node-a2t-openai.js.map +1 -0
  76. package/speechflow-cli/dst/{speechflow-node-t2a-awspolly.d.ts → speechflow-node-t2a-amazon.d.ts} +1 -1
  77. package/speechflow-cli/dst/{speechflow-node-t2a-awspolly.js → speechflow-node-t2a-amazon.js} +9 -9
  78. package/speechflow-cli/dst/speechflow-node-t2a-amazon.js.map +1 -0
  79. package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.d.ts +1 -1
  80. package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js +5 -5
  81. package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js.map +1 -1
  82. package/speechflow-cli/dst/speechflow-node-t2a-kokoro.d.ts +1 -1
  83. package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js +7 -7
  84. package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js.map +1 -1
  85. package/speechflow-cli/dst/{speechflow-node-t2t-awstranslate.d.ts → speechflow-node-t2t-amazon.d.ts} +1 -1
  86. package/speechflow-cli/dst/{speechflow-node-t2t-awstranslate.js → speechflow-node-t2t-amazon.js} +7 -7
  87. package/speechflow-cli/dst/speechflow-node-t2t-amazon.js.map +1 -0
  88. package/speechflow-cli/dst/speechflow-node-t2t-deepl.d.ts +1 -1
  89. package/speechflow-cli/dst/speechflow-node-t2t-deepl.js +5 -5
  90. package/speechflow-cli/dst/speechflow-node-t2t-deepl.js.map +1 -1
  91. package/speechflow-cli/dst/speechflow-node-t2t-format.d.ts +1 -1
  92. package/speechflow-cli/dst/speechflow-node-t2t-format.js +3 -3
  93. package/speechflow-cli/dst/speechflow-node-t2t-format.js.map +1 -1
  94. package/speechflow-cli/dst/speechflow-node-t2t-google.d.ts +1 -1
  95. package/speechflow-cli/dst/speechflow-node-t2t-google.js +8 -8
  96. package/speechflow-cli/dst/speechflow-node-t2t-google.js.map +1 -1
  97. package/speechflow-cli/dst/{speechflow-node-a2a-dynamics.d.ts → speechflow-node-t2t-modify.d.ts} +1 -5
  98. package/speechflow-cli/dst/speechflow-node-t2t-modify.js +111 -0
  99. package/speechflow-cli/dst/speechflow-node-t2t-modify.js.map +1 -0
  100. package/speechflow-cli/dst/speechflow-node-t2t-ollama.d.ts +1 -1
  101. package/speechflow-cli/dst/speechflow-node-t2t-ollama.js +5 -5
  102. package/speechflow-cli/dst/speechflow-node-t2t-ollama.js.map +1 -1
  103. package/speechflow-cli/dst/speechflow-node-t2t-openai.d.ts +1 -1
  104. package/speechflow-cli/dst/speechflow-node-t2t-openai.js +5 -5
  105. package/speechflow-cli/dst/speechflow-node-t2t-openai.js.map +1 -1
  106. package/speechflow-cli/dst/speechflow-node-t2t-sentence.d.ts +1 -1
  107. package/speechflow-cli/dst/speechflow-node-t2t-sentence.js +5 -5
  108. package/speechflow-cli/dst/speechflow-node-t2t-sentence.js.map +1 -1
  109. package/speechflow-cli/dst/speechflow-node-t2t-subtitle.d.ts +1 -1
  110. package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js +5 -5
  111. package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js.map +1 -1
  112. package/speechflow-cli/dst/speechflow-node-t2t-transformers.d.ts +1 -1
  113. package/speechflow-cli/dst/speechflow-node-t2t-transformers.js +5 -5
  114. package/speechflow-cli/dst/speechflow-node-t2t-transformers.js.map +1 -1
  115. package/speechflow-cli/dst/speechflow-node-x2x-filter.d.ts +1 -1
  116. package/speechflow-cli/dst/speechflow-node-x2x-filter.js +5 -5
  117. package/speechflow-cli/dst/speechflow-node-x2x-filter.js.map +1 -1
  118. package/speechflow-cli/dst/speechflow-node-x2x-trace.d.ts +1 -1
  119. package/speechflow-cli/dst/speechflow-node-x2x-trace.js +3 -3
  120. package/speechflow-cli/dst/speechflow-node-x2x-trace.js.map +1 -1
  121. package/speechflow-cli/dst/speechflow-node-xio-device.d.ts +1 -1
  122. package/speechflow-cli/dst/speechflow-node-xio-device.js +8 -8
  123. package/speechflow-cli/dst/speechflow-node-xio-device.js.map +1 -1
  124. package/speechflow-cli/dst/speechflow-node-xio-file.d.ts +1 -1
  125. package/speechflow-cli/dst/speechflow-node-xio-file.js +50 -29
  126. package/speechflow-cli/dst/speechflow-node-xio-file.js.map +1 -1
  127. package/speechflow-cli/dst/speechflow-node-xio-mqtt.d.ts +1 -1
  128. package/speechflow-cli/dst/speechflow-node-xio-mqtt.js +7 -7
  129. package/speechflow-cli/dst/speechflow-node-xio-mqtt.js.map +1 -1
  130. package/speechflow-cli/dst/speechflow-node-xio-websocket.d.ts +1 -1
  131. package/speechflow-cli/dst/speechflow-node-xio-websocket.js +10 -10
  132. package/speechflow-cli/dst/speechflow-node-xio-websocket.js.map +1 -1
  133. package/speechflow-cli/dst/{speechflow-utils-audio-wt.js → speechflow-util-audio-wt.js} +1 -1
  134. package/speechflow-cli/dst/speechflow-util-audio-wt.js.map +1 -0
  135. package/speechflow-cli/dst/speechflow-util-audio.d.ts +22 -0
  136. package/speechflow-cli/dst/speechflow-util-audio.js +251 -0
  137. package/speechflow-cli/dst/speechflow-util-audio.js.map +1 -0
  138. package/speechflow-cli/dst/speechflow-util-error.d.ts +14 -0
  139. package/speechflow-cli/dst/speechflow-util-error.js +131 -0
  140. package/speechflow-cli/dst/speechflow-util-error.js.map +1 -0
  141. package/speechflow-cli/dst/speechflow-util-queue.d.ts +68 -0
  142. package/speechflow-cli/dst/speechflow-util-queue.js +338 -0
  143. package/speechflow-cli/dst/speechflow-util-queue.js.map +1 -0
  144. package/speechflow-cli/dst/speechflow-util-stream.d.ts +18 -0
  145. package/speechflow-cli/dst/speechflow-util-stream.js +219 -0
  146. package/speechflow-cli/dst/speechflow-util-stream.js.map +1 -0
  147. package/speechflow-cli/dst/speechflow-util-webaudio-wt.js +124 -0
  148. package/speechflow-cli/dst/speechflow-util-webaudio-wt.js.map +1 -0
  149. package/speechflow-cli/dst/{speechflow-utils-audio.js → speechflow-util-webaudio.js} +2 -2
  150. package/speechflow-cli/dst/speechflow-util-webaudio.js.map +1 -0
  151. package/speechflow-cli/dst/speechflow-util.d.ts +4 -0
  152. package/speechflow-cli/dst/speechflow-util.js +26 -0
  153. package/speechflow-cli/dst/speechflow-util.js.map +1 -0
  154. package/speechflow-cli/dst/speechflow.js +3 -906
  155. package/speechflow-cli/dst/speechflow.js.map +1 -1
  156. package/speechflow-cli/etc/oxlint.jsonc +4 -1
  157. package/speechflow-cli/package.json +12 -11
  158. package/speechflow-cli/src/speechflow-main-api.ts +315 -0
  159. package/speechflow-cli/src/speechflow-main-cli.ts +259 -0
  160. package/speechflow-cli/src/speechflow-main-config.ts +17 -0
  161. package/speechflow-cli/src/speechflow-main-graph.ts +372 -0
  162. package/speechflow-cli/src/speechflow-main-nodes.ts +61 -0
  163. package/speechflow-cli/src/speechflow-main-status.ts +70 -0
  164. package/speechflow-cli/src/speechflow-main.ts +106 -0
  165. package/speechflow-cli/src/speechflow-node-a2a-compressor-wt.ts +4 -4
  166. package/speechflow-cli/src/speechflow-node-a2a-compressor.ts +7 -8
  167. package/speechflow-cli/src/speechflow-node-a2a-expander-wt.ts +5 -5
  168. package/speechflow-cli/src/speechflow-node-a2a-expander.ts +7 -8
  169. package/speechflow-cli/src/speechflow-node-a2a-ffmpeg.ts +7 -7
  170. package/speechflow-cli/src/speechflow-node-a2a-filler.ts +6 -6
  171. package/speechflow-cli/src/speechflow-node-a2a-gain.ts +4 -4
  172. package/speechflow-cli/src/speechflow-node-a2a-gender.ts +6 -6
  173. package/speechflow-cli/src/speechflow-node-a2a-meter.ts +4 -4
  174. package/speechflow-cli/src/speechflow-node-a2a-mute.ts +2 -2
  175. package/speechflow-cli/src/speechflow-node-a2a-rnnoise.ts +6 -6
  176. package/speechflow-cli/src/speechflow-node-a2a-speex.ts +6 -6
  177. package/speechflow-cli/src/speechflow-node-a2a-vad.ts +6 -6
  178. package/speechflow-cli/src/speechflow-node-a2a-wav.ts +2 -2
  179. package/speechflow-cli/src/{speechflow-node-a2t-awstranscribe.ts → speechflow-node-a2t-amazon.ts} +10 -10
  180. package/speechflow-cli/src/speechflow-node-a2t-deepgram.ts +7 -7
  181. package/speechflow-cli/src/{speechflow-node-a2t-openaitranscribe.ts → speechflow-node-a2t-openai.ts} +10 -10
  182. package/speechflow-cli/src/{speechflow-node-t2a-awspolly.ts → speechflow-node-t2a-amazon.ts} +7 -7
  183. package/speechflow-cli/src/speechflow-node-t2a-elevenlabs.ts +4 -4
  184. package/speechflow-cli/src/speechflow-node-t2a-kokoro.ts +6 -6
  185. package/speechflow-cli/src/{speechflow-node-t2t-awstranslate.ts → speechflow-node-t2t-amazon.ts} +5 -5
  186. package/speechflow-cli/src/speechflow-node-t2t-deepl.ts +4 -4
  187. package/speechflow-cli/src/speechflow-node-t2t-format.ts +2 -2
  188. package/speechflow-cli/src/speechflow-node-t2t-google.ts +7 -7
  189. package/speechflow-cli/src/speechflow-node-t2t-modify.ts +84 -0
  190. package/speechflow-cli/src/speechflow-node-t2t-ollama.ts +4 -4
  191. package/speechflow-cli/src/speechflow-node-t2t-openai.ts +4 -4
  192. package/speechflow-cli/src/speechflow-node-t2t-sentence.ts +4 -4
  193. package/speechflow-cli/src/speechflow-node-t2t-subtitle.ts +9 -9
  194. package/speechflow-cli/src/speechflow-node-t2t-transformers.ts +4 -4
  195. package/speechflow-cli/src/speechflow-node-x2x-filter.ts +4 -4
  196. package/speechflow-cli/src/speechflow-node-x2x-trace.ts +2 -2
  197. package/speechflow-cli/src/speechflow-node-xio-device.ts +7 -7
  198. package/speechflow-cli/src/speechflow-node-xio-file.ts +49 -28
  199. package/speechflow-cli/src/speechflow-node-xio-mqtt.ts +7 -7
  200. package/speechflow-cli/src/speechflow-node-xio-websocket.ts +9 -9
  201. package/speechflow-cli/src/{speechflow-utils-audio.ts → speechflow-util-audio.ts} +131 -1
  202. package/speechflow-cli/src/speechflow-util-error.ts +184 -0
  203. package/speechflow-cli/src/speechflow-util-queue.ts +320 -0
  204. package/speechflow-cli/src/speechflow-util-stream.ts +197 -0
  205. package/speechflow-cli/src/speechflow-util.ts +10 -0
  206. package/speechflow-cli/src/speechflow.ts +3 -947
  207. package/speechflow-ui-db/package.json +3 -3
  208. package/speechflow-ui-st/dst/app-font-fa-brands-400.woff2 +0 -0
  209. package/speechflow-ui-st/dst/app-font-fa-regular-400.woff2 +0 -0
  210. package/speechflow-ui-st/dst/app-font-fa-solid-900.woff2 +0 -0
  211. package/speechflow-ui-st/dst/app-font-fa-v4compatibility.woff2 +0 -0
  212. package/speechflow-ui-st/dst/index.css +2 -2
  213. package/speechflow-ui-st/dst/index.js +32 -33
  214. package/speechflow-ui-st/package.json +4 -4
  215. package/speechflow-cli/dst/speechflow-node-a2a-dynamics-wt.js +0 -208
  216. package/speechflow-cli/dst/speechflow-node-a2a-dynamics-wt.js.map +0 -1
  217. package/speechflow-cli/dst/speechflow-node-a2a-dynamics.js +0 -312
  218. package/speechflow-cli/dst/speechflow-node-a2a-dynamics.js.map +0 -1
  219. package/speechflow-cli/dst/speechflow-node-a2t-awstranscribe.js.map +0 -1
  220. package/speechflow-cli/dst/speechflow-node-a2t-openaitranscribe.js.map +0 -1
  221. package/speechflow-cli/dst/speechflow-node-t2a-awspolly.js.map +0 -1
  222. package/speechflow-cli/dst/speechflow-node-t2t-awstranslate.js.map +0 -1
  223. package/speechflow-cli/dst/speechflow-utils-audio-wt.js.map +0 -1
  224. package/speechflow-cli/dst/speechflow-utils-audio.js.map +0 -1
  225. package/speechflow-cli/dst/speechflow-utils.d.ts +0 -108
  226. package/speechflow-cli/dst/speechflow-utils.js +0 -740
  227. package/speechflow-cli/dst/speechflow-utils.js.map +0 -1
  228. package/speechflow-cli/src/speechflow-utils.ts +0 -804
  229. /package/speechflow-cli/dst/{speechflow-node-a2a-dynamics-wt.d.ts → speechflow-util-audio-wt.d.ts} +0 -0
  230. /package/speechflow-cli/dst/{speechflow-utils-audio-wt.d.ts → speechflow-util-webaudio-wt.d.ts} +0 -0
  231. /package/speechflow-cli/dst/{speechflow-utils-audio.d.ts → speechflow-util-webaudio.d.ts} +0 -0
  232. /package/speechflow-cli/src/{speechflow-utils-audio-wt.ts → speechflow-util-audio-wt.ts} +0 -0
@@ -18,7 +18,7 @@ import HAPIWebSocket from "hapi-plugin-websocket"
18
18
 
19
19
  /* internal dependencies */
20
20
  import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
21
- import * as utils from "./speechflow-utils"
21
+ import * as util from "./speechflow-util"
22
22
 
23
23
  type wsPeerCtx = {
24
24
  peer: string
@@ -30,9 +30,9 @@ type wsPeerInfo = {
30
30
  }
31
31
 
32
32
  /* SpeechFlow node for subtitle (text-to-text) "translations" */
33
- export default class SpeechFlowNodeSubtitle extends SpeechFlowNode {
33
+ export default class SpeechFlowNodeT2TSubtitle extends SpeechFlowNode {
34
34
  /* declare official node name */
35
- public static name = "subtitle"
35
+ public static name = "t2t-subtitle"
36
36
 
37
37
  /* internal state */
38
38
  private sequenceNo = 1
@@ -44,11 +44,11 @@ export default class SpeechFlowNodeSubtitle extends SpeechFlowNode {
44
44
 
45
45
  /* declare node configuration parameters */
46
46
  this.configure({
47
- format: { type: "string", pos: 0, val: "srt", match: /^(?:srt|vtt)$/ },
48
- words: { type: "boolean", val: false },
49
- mode: { type: "string", val: "export", match: /^(?:export|render)$/ },
50
- addr: { type: "string", val: "127.0.0.1" },
51
- port: { type: "number", val: 8585 }
47
+ format: { type: "string", pos: 0, val: "srt", match: /^(?:srt|vtt)$/ },
48
+ words: { type: "boolean", val: false },
49
+ mode: { type: "string", val: "export", match: /^(?:export|render)$/ },
50
+ addr: { type: "string", val: "127.0.0.1" },
51
+ port: { type: "number", val: 8585 }
52
52
  })
53
53
 
54
54
  /* declare node input/output format */
@@ -147,7 +147,7 @@ export default class SpeechFlowNodeSubtitle extends SpeechFlowNode {
147
147
  this.push(chunkNew)
148
148
  callback()
149
149
  }).catch((error: unknown) => {
150
- callback(utils.ensureError(error))
150
+ callback(util.ensureError(error))
151
151
  })
152
152
  }
153
153
  }
@@ -13,16 +13,16 @@ import * as Transformers from "@huggingface/transformers"
13
13
 
14
14
  /* internal dependencies */
15
15
  import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
16
- import * as utils from "./speechflow-utils"
16
+ import * as util from "./speechflow-util"
17
17
 
18
18
  /* internal utility types */
19
19
  type ConfigEntry = { systemPrompt: string, chat: Array<{ role: string, content: string }> }
20
20
  type Config = { [ key: string ]: ConfigEntry }
21
21
 
22
22
  /* SpeechFlow node for Transformers text-to-text translation */
23
- export default class SpeechFlowNodeTransformers extends SpeechFlowNode {
23
+ export default class SpeechFlowNodeT2TTransformers extends SpeechFlowNode {
24
24
  /* declare official node name */
25
- public static name = "transformers"
25
+ public static name = "t2t-transformers"
26
26
 
27
27
  /* internal state */
28
28
  private translator: Transformers.TranslationPipeline | null = null
@@ -214,7 +214,7 @@ export default class SpeechFlowNodeTransformers extends SpeechFlowNode {
214
214
  this.push(chunk)
215
215
  callback()
216
216
  }).catch((error: unknown) => {
217
- callback(utils.ensureError(error))
217
+ callback(util.ensureError(error))
218
218
  })
219
219
  }
220
220
  },
@@ -9,15 +9,15 @@ import Stream from "node:stream"
9
9
 
10
10
  /* internal dependencies */
11
11
  import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
12
- import * as utils from "./speechflow-utils"
12
+ import * as util from "./speechflow-util"
13
13
 
14
14
  /* SpeechFlow node for data flow filtering (based on meta information) */
15
- export default class SpeechFlowNodeFilter extends SpeechFlowNode {
15
+ export default class SpeechFlowNodeX2XFilter extends SpeechFlowNode {
16
16
  /* declare official node name */
17
- public static name = "filter"
17
+ public static name = "x2x-filter"
18
18
 
19
19
  /* cached regular expression instance */
20
- private cachedRegExp = new utils.CachedRegExp()
20
+ private cachedRegExp = new util.CachedRegExp()
21
21
 
22
22
  /* construct node */
23
23
  constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
@@ -14,9 +14,9 @@ import { Duration } from "luxon"
14
14
  import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
15
15
 
16
16
  /* SpeechFlow node for data flow tracing */
17
- export default class SpeechFlowNodeTrace extends SpeechFlowNode {
17
+ export default class SpeechFlowNodeX2XTrace extends SpeechFlowNode {
18
18
  /* declare official node name */
19
- public static name = "trace"
19
+ public static name = "x2x-trace"
20
20
 
21
21
  /* construct node */
22
22
  constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
@@ -12,12 +12,12 @@ import PortAudio from "@gpeng/naudiodon"
12
12
 
13
13
  /* internal dependencies */
14
14
  import SpeechFlowNode from "./speechflow-node"
15
- import * as utils from "./speechflow-utils"
15
+ import * as util from "./speechflow-util"
16
16
 
17
17
  /* SpeechFlow node for device access */
18
- export default class SpeechFlowNodeDevice extends SpeechFlowNode {
18
+ export default class SpeechFlowNodeXIODevice extends SpeechFlowNode {
19
19
  /* declare official node name */
20
- public static name = "device"
20
+ public static name = "xio-device"
21
21
 
22
22
  /* internal state */
23
23
  private io: PortAudio.IoStreamRead
@@ -135,8 +135,8 @@ export default class SpeechFlowNodeDevice extends SpeechFlowNode {
135
135
  this.stream = this.io as unknown as Stream.Duplex
136
136
 
137
137
  /* convert regular stream into object-mode stream */
138
- const wrapper1 = utils.createTransformStreamForWritableSide()
139
- const wrapper2 = utils.createTransformStreamForReadableSide("audio", () => this.timeZero)
138
+ const wrapper1 = util.createTransformStreamForWritableSide()
139
+ const wrapper2 = util.createTransformStreamForReadableSide("audio", () => this.timeZero)
140
140
  this.stream = Stream.compose(wrapper1, this.stream, wrapper2)
141
141
  }
142
142
  else if (this.params.mode === "r") {
@@ -156,7 +156,7 @@ export default class SpeechFlowNodeDevice extends SpeechFlowNode {
156
156
  this.stream = this.io as unknown as Stream.Readable
157
157
 
158
158
  /* convert regular stream into object-mode stream */
159
- const wrapper = utils.createTransformStreamForReadableSide("audio", () => this.timeZero)
159
+ const wrapper = util.createTransformStreamForReadableSide("audio", () => this.timeZero)
160
160
  this.stream = Stream.compose(this.stream, wrapper)
161
161
  }
162
162
  else if (this.params.mode === "w") {
@@ -176,7 +176,7 @@ export default class SpeechFlowNodeDevice extends SpeechFlowNode {
176
176
  this.stream = this.io as unknown as Stream.Writable
177
177
 
178
178
  /* convert regular stream into object-mode stream */
179
- const wrapper = utils.createTransformStreamForWritableSide()
179
+ const wrapper = util.createTransformStreamForWritableSide()
180
180
  this.stream = Stream.compose(wrapper, this.stream)
181
181
  }
182
182
  else
@@ -10,12 +10,12 @@ import Stream from "node:stream"
10
10
 
11
11
  /* internal dependencies */
12
12
  import SpeechFlowNode from "./speechflow-node"
13
- import * as utils from "./speechflow-utils"
13
+ import * as util from "./speechflow-util"
14
14
 
15
15
  /* SpeechFlow node for file access */
16
- export default class SpeechFlowNodeFile extends SpeechFlowNode {
16
+ export default class SpeechFlowNodeXIOFile extends SpeechFlowNode {
17
17
  /* declare official node name */
18
- public static name = "file"
18
+ public static name = "xio-file"
19
19
 
20
20
  /* construct node */
21
21
  constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
@@ -59,6 +59,27 @@ export default class SpeechFlowNodeFile extends SpeechFlowNode {
59
59
  if (this.params.path === "")
60
60
  throw new Error("required parameter \"path\" has to be given")
61
61
 
62
+ /* utility function: create a writable stream as chunker that
63
+ writes to process.stdout but properly handles finish events.
64
+ This ensures the writable side of the composed stream below
65
+ properly signals completion while keeping process.stdout open
66
+ (as it's a global stream that shouldn't be closed by individual nodes). */
67
+ const createStdoutChunker = () =>
68
+ new Stream.Writable({
69
+ highWaterMark: this.params.type === "audio" ?
70
+ highWaterMarkAudio : highWaterMarkText,
71
+ write (chunk: Buffer | string, encoding, callback) {
72
+ const canContinue = process.stdout.write(chunk, encoding)
73
+ if (canContinue)
74
+ callback()
75
+ else
76
+ process.stdout.once("drain", callback)
77
+ },
78
+ final (callback) {
79
+ callback()
80
+ }
81
+ })
82
+
62
83
  /* dispatch according to mode and path */
63
84
  if (this.params.mode === "rw") {
64
85
  if (this.params.path === "-") {
@@ -107,8 +128,8 @@ export default class SpeechFlowNodeFile extends SpeechFlowNode {
107
128
  }
108
129
 
109
130
  /* convert regular stream into object-mode stream */
110
- const wrapper1 = utils.createTransformStreamForWritableSide()
111
- const wrapper2 = utils.createTransformStreamForReadableSide(
131
+ const wrapper1 = util.createTransformStreamForWritableSide()
132
+ const wrapper2 = util.createTransformStreamForReadableSide(
112
133
  this.params.type, () => this.timeZero)
113
134
  this.stream = Stream.compose(wrapper1, this.stream, wrapper2)
114
135
  }
@@ -124,7 +145,7 @@ export default class SpeechFlowNodeFile extends SpeechFlowNode {
124
145
  process.stdin.setEncoding(this.config.textEncoding)
125
146
  chunker = new Stream.PassThrough({ highWaterMark: highWaterMarkText })
126
147
  }
127
- const wrapper = utils.createTransformStreamForReadableSide(
148
+ const wrapper = util.createTransformStreamForReadableSide(
128
149
  this.params.type, () => this.timeZero)
129
150
  this.stream = Stream.compose(process.stdin, chunker, wrapper)
130
151
  }
@@ -137,7 +158,7 @@ export default class SpeechFlowNodeFile extends SpeechFlowNode {
137
158
  else
138
159
  readable = fs.createReadStream(this.params.path,
139
160
  { highWaterMark: highWaterMarkText, encoding: this.config.textEncoding })
140
- const wrapper = utils.createTransformStreamForReadableSide(
161
+ const wrapper = util.createTransformStreamForReadableSide(
141
162
  this.params.type, () => this.timeZero)
142
163
  this.stream = Stream.compose(readable, wrapper)
143
164
  }
@@ -145,17 +166,13 @@ export default class SpeechFlowNodeFile extends SpeechFlowNode {
145
166
  else if (this.params.mode === "w") {
146
167
  if (this.params.path === "-") {
147
168
  /* standard I/O */
148
- let chunker: Stream.PassThrough
149
- if (this.params.type === "audio") {
169
+ if (this.params.type === "audio")
150
170
  process.stdout.setEncoding()
151
- chunker = new Stream.PassThrough({ highWaterMark: highWaterMarkAudio })
152
- }
153
- else {
171
+ else
154
172
  process.stdout.setEncoding(this.config.textEncoding)
155
- chunker = new Stream.PassThrough({ highWaterMark: highWaterMarkText })
156
- }
157
- const wrapper = utils.createTransformStreamForWritableSide()
158
- this.stream = Stream.compose(wrapper, chunker, process.stdout)
173
+ const chunker = createStdoutChunker()
174
+ const wrapper = util.createTransformStreamForWritableSide()
175
+ this.stream = Stream.compose(wrapper, chunker)
159
176
  }
160
177
  else {
161
178
  /* file I/O */
@@ -166,7 +183,7 @@ export default class SpeechFlowNodeFile extends SpeechFlowNode {
166
183
  else
167
184
  writable = fs.createWriteStream(this.params.path,
168
185
  { highWaterMark: highWaterMarkText, encoding: this.config.textEncoding })
169
- const wrapper = utils.createTransformStreamForWritableSide()
186
+ const wrapper = util.createTransformStreamForWritableSide()
170
187
  this.stream = Stream.compose(wrapper, writable)
171
188
  }
172
189
  }
@@ -178,18 +195,22 @@ export default class SpeechFlowNodeFile extends SpeechFlowNode {
178
195
  async close () {
179
196
  /* shutdown stream */
180
197
  if (this.stream !== null) {
181
- await new Promise<void>((resolve, reject) => {
182
- if (this.stream instanceof Stream.Writable || this.stream instanceof Stream.Duplex) {
183
- this.stream.end((err?: Error) => {
184
- if (err)
185
- reject(err)
186
- else
198
+ await Promise.race([
199
+ new Promise<void>((resolve, reject) => {
200
+ if (this.stream instanceof Stream.Writable || this.stream instanceof Stream.Duplex) {
201
+ if (this.stream.writableEnded || this.stream.destroyed)
187
202
  resolve()
188
- })
189
- }
190
- else
191
- resolve()
192
- })
203
+ else
204
+ this.stream.end((err?: Error) => {
205
+ if (err) reject(err)
206
+ else resolve()
207
+ })
208
+ }
209
+ else
210
+ resolve()
211
+ }),
212
+ new Promise<void>((resolve) => setTimeout(() => resolve(), 5000))
213
+ ])
193
214
  if (this.params.path !== "-")
194
215
  this.stream.destroy()
195
216
  this.stream = null
@@ -13,17 +13,17 @@ import UUID from "pure-uuid"
13
13
 
14
14
  /* internal dependencies */
15
15
  import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
16
- import * as utils from "./speechflow-utils"
16
+ import * as util from "./speechflow-util"
17
17
 
18
18
  /* SpeechFlow node for MQTT networking */
19
- export default class SpeechFlowNodeMQTT extends SpeechFlowNode {
19
+ export default class SpeechFlowNodeXIOMQTT extends SpeechFlowNode {
20
20
  /* declare official node name */
21
- public static name = "mqtt"
21
+ public static name = "xio-mqtt"
22
22
 
23
23
  /* internal state */
24
24
  private broker: MQTT.MqttClient | null = null
25
25
  private clientId: string = (new UUID(1)).format()
26
- private chunkQueue: utils.SingleQueue<SpeechFlowChunk> | null = null
26
+ private chunkQueue: util.SingleQueue<SpeechFlowChunk> | null = null
27
27
 
28
28
  /* construct node */
29
29
  constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
@@ -99,12 +99,12 @@ export default class SpeechFlowNodeMQTT extends SpeechFlowNode {
99
99
  this.broker.on("disconnect", (packet: MQTT.IDisconnectPacket) => {
100
100
  this.log("info", `connection closed to MQTT ${this.params.url}`)
101
101
  })
102
- this.chunkQueue = new utils.SingleQueue<SpeechFlowChunk>()
102
+ this.chunkQueue = new util.SingleQueue<SpeechFlowChunk>()
103
103
  this.broker.on("message", (topic: string, payload: Buffer, packet: MQTT.IPublishPacket) => {
104
104
  if (topic !== this.params.topicRead || this.params.mode === "w")
105
105
  return
106
106
  try {
107
- const chunk = utils.streamChunkDecode(payload)
107
+ const chunk = util.streamChunkDecode(payload)
108
108
  this.chunkQueue!.write(chunk)
109
109
  }
110
110
  catch (_err: any) {
@@ -125,7 +125,7 @@ export default class SpeechFlowNodeMQTT extends SpeechFlowNode {
125
125
  else if (!self.broker!.connected)
126
126
  callback(new Error("still no MQTT connection available"))
127
127
  else {
128
- const data = Buffer.from(utils.streamChunkEncode(chunk))
128
+ const data = Buffer.from(util.streamChunkEncode(chunk))
129
129
  self.broker!.publish(self.params.topicWrite, data, { qos: 2, retain: false }, (err) => {
130
130
  if (err)
131
131
  callback(new Error(`failed to publish to MQTT topic "${self.params.topicWrite}": ${err}`))
@@ -13,12 +13,12 @@ import ReconnWebSocket, { ErrorEvent } from "@opensumi/reconnecting-websocket"
13
13
 
14
14
  /* internal dependencies */
15
15
  import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
16
- import * as utils from "./speechflow-utils"
16
+ import * as util from "./speechflow-util"
17
17
 
18
18
  /* SpeechFlow node for Websocket networking */
19
- export default class SpeechFlowNodeWebsocket extends SpeechFlowNode {
19
+ export default class SpeechFlowNodeXIOWebSocket extends SpeechFlowNode {
20
20
  /* declare official node name */
21
- public static name = "websocket"
21
+ public static name = "xio-websocket"
22
22
 
23
23
  /* internal state */
24
24
  private server: ws.WebSocketServer | null = null
@@ -63,7 +63,7 @@ export default class SpeechFlowNodeWebsocket extends SpeechFlowNode {
63
63
  /* listen locally on a Websocket port */
64
64
  const url = new URL(this.params.listen)
65
65
  const websockets = new Set<ws.WebSocket>()
66
- const chunkQueue = new utils.SingleQueue<SpeechFlowChunk>()
66
+ const chunkQueue = new util.SingleQueue<SpeechFlowChunk>()
67
67
  this.server = new ws.WebSocketServer({
68
68
  host: url.hostname,
69
69
  port: Number.parseInt(url.port, 10),
@@ -101,7 +101,7 @@ export default class SpeechFlowNodeWebsocket extends SpeechFlowNode {
101
101
  buffer = Buffer.from(data)
102
102
  else
103
103
  buffer = Buffer.concat(data)
104
- const chunk = utils.streamChunkDecode(buffer)
104
+ const chunk = util.streamChunkDecode(buffer)
105
105
  chunkQueue.write(chunk)
106
106
  })
107
107
  })
@@ -122,7 +122,7 @@ export default class SpeechFlowNodeWebsocket extends SpeechFlowNode {
122
122
  else if (websockets.size === 0)
123
123
  callback(new Error("still no Websocket connections available"))
124
124
  else {
125
- const data = utils.streamChunkEncode(chunk)
125
+ const data = util.streamChunkEncode(chunk)
126
126
  const results: Promise<void>[] = []
127
127
  for (const websocket of websockets.values()) {
128
128
  results.push(new Promise<void>((resolve, reject) => {
@@ -172,7 +172,7 @@ export default class SpeechFlowNodeWebsocket extends SpeechFlowNode {
172
172
  this.client.addEventListener("error", (ev: ErrorEvent) => {
173
173
  this.log("error", `error of connection on URL ${this.params.connect}: ${ev.error.message}`)
174
174
  })
175
- const chunkQueue = new utils.SingleQueue<SpeechFlowChunk>()
175
+ const chunkQueue = new util.SingleQueue<SpeechFlowChunk>()
176
176
  this.client.addEventListener("message", (ev: MessageEvent) => {
177
177
  if (this.params.mode === "w") {
178
178
  this.log("warning", `connection to URL ${this.params.connect}: ` +
@@ -185,7 +185,7 @@ export default class SpeechFlowNodeWebsocket extends SpeechFlowNode {
185
185
  return
186
186
  }
187
187
  const buffer = Buffer.from(ev.data)
188
- const chunk = utils.streamChunkDecode(buffer)
188
+ const chunk = util.streamChunkDecode(buffer)
189
189
  chunkQueue.write(chunk)
190
190
  })
191
191
  this.client.binaryType = "arraybuffer"
@@ -203,7 +203,7 @@ export default class SpeechFlowNodeWebsocket extends SpeechFlowNode {
203
203
  else if (!self.client!.OPEN)
204
204
  callback(new Error("still no Websocket connection available"))
205
205
  else {
206
- const data = utils.streamChunkEncode(chunk)
206
+ const data = util.streamChunkEncode(chunk)
207
207
  self.client!.send(data)
208
208
  callback()
209
209
  }
@@ -10,6 +10,136 @@ import path from "node:path"
10
10
  /* external dependencies */
11
11
  import { AudioContext, AudioWorkletNode } from "node-web-audio-api"
12
12
 
13
+ /* calculate duration of an audio buffer */
14
+ export function audioBufferDuration (
15
+ buffer: Buffer,
16
+ sampleRate = 48000,
17
+ bitDepth = 16,
18
+ channels = 1,
19
+ littleEndian = true
20
+ ) {
21
+ /* sanity check parameters */
22
+ if (!Buffer.isBuffer(buffer))
23
+ throw new Error("invalid input (Buffer expected)")
24
+ if (littleEndian !== true)
25
+ throw new Error("only Little Endian supported")
26
+ if (sampleRate <= 0)
27
+ throw new Error("sample rate must be positive")
28
+ if (bitDepth <= 0 || bitDepth % 8 !== 0)
29
+ throw new Error("bit depth must be positive and multiple of 8")
30
+ if (channels <= 0)
31
+ throw new Error("channels must be positive")
32
+
33
+ /* calculate duration */
34
+ const bytesPerSample = bitDepth / 8
35
+ const totalSamples = buffer.length / (bytesPerSample * channels)
36
+ return totalSamples / sampleRate
37
+ }
38
+
39
+ /* calculate duration of an audio array */
40
+ export function audioArrayDuration (
41
+ arr: Float32Array,
42
+ sampleRate = 48000,
43
+ channels = 1
44
+ ) {
45
+ /* sanity check parameters */
46
+ if (arr.length === 0)
47
+ return 0
48
+ if (sampleRate <= 0)
49
+ throw new Error("sample rate must be positive")
50
+ if (channels <= 0)
51
+ throw new Error("channels must be positive")
52
+
53
+ /* calculate duration */
54
+ const totalSamples = arr.length / channels
55
+ return totalSamples / sampleRate
56
+ }
57
+
58
+ /* helper function: convert Buffer in PCM/I16 to Float32Array in PCM/F32 format */
59
+ export function convertBufToF32 (buf: Buffer, littleEndian = true) {
60
+ if (buf.length % 2 !== 0)
61
+ throw new Error("buffer length must be even for 16-bit samples")
62
+ const dataView = new DataView(buf.buffer)
63
+ const arr = new Float32Array(buf.length / 2)
64
+ for (let i = 0; i < arr.length; i++)
65
+ arr[i] = dataView.getInt16(i * 2, littleEndian) / 32768
66
+ return arr
67
+ }
68
+
69
+ /* helper function: convert Float32Array in PCM/F32 to Buffer in PCM/I16 format */
70
+ export function convertF32ToBuf (arr: Float32Array) {
71
+ if (arr.length === 0)
72
+ return Buffer.alloc(0)
73
+ const int16Array = new Int16Array(arr.length)
74
+ for (let i = 0; i < arr.length; i++) {
75
+ let sample = arr[i]
76
+ if (Number.isNaN(sample))
77
+ sample = 0
78
+ int16Array[i] = Math.max(-32768, Math.min(32767, Math.round(sample * 32768)))
79
+ }
80
+ return Buffer.from(int16Array.buffer)
81
+ }
82
+
83
+ /* helper function: convert Buffer in PCM/I16 to Int16Array */
84
+ export function convertBufToI16 (buf: Buffer, littleEndian = true) {
85
+ if (buf.length % 2 !== 0)
86
+ throw new Error("buffer length must be even for 16-bit samples")
87
+ const dataView = new DataView(buf.buffer, buf.byteOffset, buf.byteLength)
88
+ const arr = new Int16Array(buf.length / 2)
89
+ for (let i = 0; i < buf.length / 2; i++)
90
+ arr[i] = dataView.getInt16(i * 2, littleEndian)
91
+ return arr
92
+ }
93
+
94
+ /* helper function: convert In16Array in PCM/I16 to Buffer */
95
+ export function convertI16ToBuf (arr: Int16Array, littleEndian = true) {
96
+ if (arr.length === 0)
97
+ return Buffer.alloc(0)
98
+ const buf = Buffer.allocUnsafe(arr.length * 2)
99
+ for (let i = 0; i < arr.length; i++) {
100
+ if (littleEndian)
101
+ buf.writeInt16LE(arr[i], i * 2)
102
+ else
103
+ buf.writeInt16BE(arr[i], i * 2)
104
+ }
105
+ return buf
106
+ }
107
+
108
+ /* process Int16Array in fixed-size segments */
109
+ export async function processInt16ArrayInSegments (
110
+ data: Int16Array<ArrayBuffer>,
111
+ segmentSize: number,
112
+ processor: (segment: Int16Array<ArrayBuffer>) => Promise<Int16Array<ArrayBuffer>>
113
+ ): Promise<Int16Array<ArrayBuffer>> {
114
+ /* process full segments */
115
+ let i = 0
116
+ while ((i + segmentSize) <= data.length) {
117
+ const segment = data.slice(i, i + segmentSize)
118
+ const result = await processor(segment)
119
+ data.set(result, i)
120
+ i += segmentSize
121
+ }
122
+
123
+ /* process final partial segment if it exists */
124
+ if (i < data.length) {
125
+ const len = data.length - i
126
+ const segment = new Int16Array(segmentSize)
127
+ segment.set(data.slice(i), 0)
128
+ segment.fill(0, len, segmentSize)
129
+ const result = await processor(segment)
130
+ data.set(result.slice(0, len), i)
131
+ }
132
+ return data
133
+ }
134
+
135
+ /* helper functions for linear/decibel conversions */
136
+ export function lin2dB (x: number): number {
137
+ return 20 * Math.log10(Math.max(x, 1e-12))
138
+ }
139
+ export function dB2lin (db: number): number {
140
+ return Math.pow(10, db / 20)
141
+ }
142
+
13
143
  export class WebAudio {
14
144
  /* internal state */
15
145
  public audioContext: AudioContext
@@ -40,7 +170,7 @@ export class WebAudio {
40
170
  await this.audioContext.resume()
41
171
 
42
172
  /* add audio worklet module */
43
- const url = path.resolve(__dirname, "speechflow-utils-audio-wt.js")
173
+ const url = path.resolve(__dirname, "speechflow-util-audio-wt.js")
44
174
  await this.audioContext.audioWorklet.addModule(url)
45
175
 
46
176
  /* create source node */