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
@@ -1,804 +0,0 @@
1
- /*
2
- ** SpeechFlow - Speech Processing Flow Graph
3
- ** Copyright (c) 2024-2025 Dr. Ralf S. Engelschall <rse@engelschall.com>
4
- ** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
5
- */
6
-
7
- /* standard dependencies */
8
- import Stream from "node:stream"
9
- import { EventEmitter } from "node:events"
10
- import { type, type Type } from "arktype"
11
-
12
- /* external dependencies */
13
- import { DateTime, Duration } from "luxon"
14
- import * as CBOR from "cbor2"
15
- import * as IntervalTree from "node-interval-tree"
16
-
17
- /* internal dependencies */
18
- import { SpeechFlowChunk } from "./speechflow-node"
19
-
20
- /* helper function for retrieving an Error object */
21
- export function ensureError (error: unknown, prefix?: string): Error {
22
- if (error instanceof Error && prefix === undefined)
23
- return error
24
- let msg = error instanceof Error ?
25
- error.message : String(error)
26
- if (prefix)
27
- msg = `${prefix}: ${msg}`
28
- return new Error(msg, { cause: error })
29
- }
30
-
31
- /* helper function for retrieving a Promise object */
32
- export function ensurePromise<T> (arg: T | Promise<T>): Promise<T> {
33
- if (!(arg instanceof Promise))
34
- arg = Promise.resolve(arg)
35
- return arg
36
- }
37
-
38
- /* helper function for running the finally code of "run" */
39
- function runFinally (onfinally?: () => void) {
40
- if (!onfinally)
41
- return
42
- try { onfinally() }
43
- catch (_arg: unknown) { /* ignored */ }
44
- }
45
-
46
- /* helper type for ensuring T contains no Promise */
47
- type runNoPromise<T> =
48
- [ T ] extends [ Promise<any> ] ? never : T
49
-
50
- /* run a synchronous or asynchronous action */
51
- export function run<T, X extends runNoPromise<T> | never> (
52
- action: () => X,
53
- oncatch?: (error: Error) => X | never,
54
- onfinally?: () => void
55
- ): X
56
- export function run<T, X extends runNoPromise<T> | never> (
57
- description: string,
58
- action: () => X,
59
- oncatch?: (error: Error) => X | never,
60
- onfinally?: () => void
61
- ): X
62
- export function run<T, X extends (T | Promise<T>)> (
63
- action: () => X,
64
- oncatch?: (error: Error) => X,
65
- onfinally?: () => void
66
- ): Promise<T>
67
- export function run<T, X extends (T | Promise<T>)> (
68
- description: string,
69
- action: () => X,
70
- oncatch?: (error: Error) => X,
71
- onfinally?: () => void
72
- ): Promise<T>
73
- export function run<T> (
74
- ...args: any[]
75
- ): T | Promise<T> | never {
76
- /* support overloaded signatures */
77
- let description: string | undefined
78
- let action: () => T | Promise<T> | never
79
- let oncatch: (error: Error) => T | Promise<T> | never
80
- let onfinally: () => void
81
- if (typeof args[0] === "string") {
82
- description = args[0]
83
- action = args[1]
84
- oncatch = args[2]
85
- onfinally = args[3]
86
- }
87
- else {
88
- action = args[0]
89
- oncatch = args[1]
90
- onfinally = args[2]
91
- }
92
-
93
- /* perform the action */
94
- let result: T | Promise<T>
95
- try {
96
- result = action()
97
- }
98
- catch (arg: unknown) {
99
- /* synchronous case (error branch) */
100
- let error = ensureError(arg, description)
101
- if (oncatch) {
102
- try {
103
- result = oncatch(error)
104
- }
105
- catch (arg: unknown) {
106
- error = ensureError(arg, description)
107
- runFinally(onfinally)
108
- throw error
109
- }
110
- runFinally(onfinally)
111
- return result
112
- }
113
- runFinally(onfinally)
114
- throw error
115
- }
116
- if (result instanceof Promise) {
117
- /* asynchronous case (result or error branch) */
118
- return result.catch((arg: unknown) => {
119
- /* asynchronous case (error branch) */
120
- let error = ensureError(arg, description)
121
- if (oncatch) {
122
- try {
123
- return ensurePromise(oncatch(error))
124
- }
125
- catch (arg: unknown) {
126
- error = ensureError(arg, description)
127
- return Promise.reject(error)
128
- }
129
- }
130
- return Promise.reject(error)
131
- }).finally(() => {
132
- /* asynchronous case (result and error branch) */
133
- runFinally(onfinally)
134
- })
135
- }
136
- else {
137
- /* synchronous case (result branch) */
138
- runFinally(onfinally)
139
- return result
140
- }
141
- }
142
-
143
- /* run a synchronous or asynchronous action */
144
- /* eslint @typescript-eslint/unified-signatures: off */
145
- export function runner<T, X extends runNoPromise<T> | never, F extends (...args: any[]) => X> (
146
- action: F,
147
- oncatch?: (error: Error) => X | never,
148
- onfinally?: () => void
149
- ): F
150
- export function runner<T, X extends runNoPromise<T> | never, F extends (...args: any[]) => X> (
151
- description: string,
152
- action: F,
153
- oncatch?: (error: Error) => X | never,
154
- onfinally?: () => void
155
- ): F
156
- export function runner<T, X extends (T | Promise<T>), F extends (...args: any[]) => Promise<T>> (
157
- action: F,
158
- oncatch?: (error: Error) => X,
159
- onfinally?: () => void
160
- ): F
161
- export function runner<T, X extends (T | Promise<T>), F extends (...args: any[]) => Promise<T>> (
162
- description: string,
163
- action: F,
164
- oncatch?: (error: Error) => X,
165
- onfinally?: () => void
166
- ): F
167
- export function runner<T> (
168
- ...args: any[]
169
- ): (...args: any[]) => T | Promise<T> | never {
170
- /* support overloaded signatures */
171
- let description: string | undefined
172
- let action: (...args: any[]) => T | Promise<T> | never
173
- let oncatch: (error: Error) => T | Promise<T> | never
174
- let onfinally: () => void
175
- if (typeof args[0] === "string") {
176
- description = args[0]
177
- action = args[1]
178
- oncatch = args[2]
179
- onfinally = args[3]
180
- }
181
- else {
182
- action = args[0]
183
- oncatch = args[1]
184
- onfinally = args[2]
185
- }
186
-
187
- /* wrap the "run" operation on "action" into function
188
- which exposes the signature of "action" */
189
- return (...args: any[]) => {
190
- if (description)
191
- return run(description, () => action(...args), oncatch, onfinally)
192
- else
193
- return run(() => action(...args), oncatch, onfinally)
194
- }
195
- }
196
-
197
- /* import an object with parsing and strict error handling */
198
- export function importObject<T>(name: string, arg: object | string, validator: Type<T, {}>): T {
199
- const obj: object = typeof arg === "string" ?
200
- run(`${name}: parsing JSON`, () => JSON.parse(arg)) :
201
- arg
202
- const result = validator(obj)
203
- if (result instanceof type.errors)
204
- throw new Error(`${name}: validation: ${result.summary}`)
205
- return result as T
206
- }
207
-
208
- /* calculate duration of an audio buffer */
209
- export function audioBufferDuration (
210
- buffer: Buffer,
211
- sampleRate = 48000,
212
- bitDepth = 16,
213
- channels = 1,
214
- littleEndian = true
215
- ) {
216
- /* sanity check parameters */
217
- if (!Buffer.isBuffer(buffer))
218
- throw new Error("invalid input (Buffer expected)")
219
- if (littleEndian !== true)
220
- throw new Error("only Little Endian supported")
221
- if (sampleRate <= 0)
222
- throw new Error("sample rate must be positive")
223
- if (bitDepth <= 0 || bitDepth % 8 !== 0)
224
- throw new Error("bit depth must be positive and multiple of 8")
225
- if (channels <= 0)
226
- throw new Error("channels must be positive")
227
-
228
- /* calculate duration */
229
- const bytesPerSample = bitDepth / 8
230
- const totalSamples = buffer.length / (bytesPerSample * channels)
231
- return totalSamples / sampleRate
232
- }
233
-
234
- /* calculate duration of an audio array */
235
- export function audioArrayDuration (
236
- arr: Float32Array,
237
- sampleRate = 48000,
238
- channels = 1
239
- ) {
240
- /* sanity check parameters */
241
- if (arr.length === 0)
242
- return 0
243
- if (sampleRate <= 0)
244
- throw new Error("sample rate must be positive")
245
- if (channels <= 0)
246
- throw new Error("channels must be positive")
247
-
248
- /* calculate duration */
249
- const totalSamples = arr.length / channels
250
- return totalSamples / sampleRate
251
- }
252
-
253
- /* helper function: convert Buffer in PCM/I16 to Float32Array in PCM/F32 format */
254
- export function convertBufToF32 (buf: Buffer, littleEndian = true) {
255
- if (buf.length % 2 !== 0)
256
- throw new Error("buffer length must be even for 16-bit samples")
257
- const dataView = new DataView(buf.buffer)
258
- const arr = new Float32Array(buf.length / 2)
259
- for (let i = 0; i < arr.length; i++)
260
- arr[i] = dataView.getInt16(i * 2, littleEndian) / 32768
261
- return arr
262
- }
263
-
264
- /* helper function: convert Float32Array in PCM/F32 to Buffer in PCM/I16 format */
265
- export function convertF32ToBuf (arr: Float32Array) {
266
- if (arr.length === 0)
267
- return Buffer.alloc(0)
268
- const int16Array = new Int16Array(arr.length)
269
- for (let i = 0; i < arr.length; i++) {
270
- let sample = arr[i]
271
- if (Number.isNaN(sample))
272
- sample = 0
273
- int16Array[i] = Math.max(-32768, Math.min(32767, Math.round(sample * 32768)))
274
- }
275
- return Buffer.from(int16Array.buffer)
276
- }
277
-
278
- /* helper function: convert Buffer in PCM/I16 to Int16Array */
279
- export function convertBufToI16 (buf: Buffer, littleEndian = true) {
280
- if (buf.length % 2 !== 0)
281
- throw new Error("buffer length must be even for 16-bit samples")
282
- const dataView = new DataView(buf.buffer, buf.byteOffset, buf.byteLength)
283
- const arr = new Int16Array(buf.length / 2)
284
- for (let i = 0; i < buf.length / 2; i++)
285
- arr[i] = dataView.getInt16(i * 2, littleEndian)
286
- return arr
287
- }
288
-
289
- /* helper function: convert In16Array in PCM/I16 to Buffer */
290
- export function convertI16ToBuf (arr: Int16Array, littleEndian = true) {
291
- if (arr.length === 0)
292
- return Buffer.alloc(0)
293
- const buf = Buffer.allocUnsafe(arr.length * 2)
294
- for (let i = 0; i < arr.length; i++) {
295
- if (littleEndian)
296
- buf.writeInt16LE(arr[i], i * 2)
297
- else
298
- buf.writeInt16BE(arr[i], i * 2)
299
- }
300
- return buf
301
- }
302
-
303
- /* create a Duplex/Transform stream which has
304
- object-mode on Writable side and buffer/string-mode on Readable side */
305
- export function createTransformStreamForWritableSide () {
306
- return new Stream.Transform({
307
- readableObjectMode: true,
308
- writableObjectMode: true,
309
- decodeStrings: false,
310
- highWaterMark: 1,
311
- transform (chunk: SpeechFlowChunk, encoding, callback) {
312
- this.push(chunk.payload)
313
- callback()
314
- },
315
- final (callback) {
316
- this.push(null)
317
- callback()
318
- }
319
- })
320
- }
321
-
322
- /* create a Duplex/Transform stream which has
323
- object-mode on Readable side and buffer/string-mode on Writable side */
324
- export function createTransformStreamForReadableSide (type: "text" | "audio", getTimeZero: () => DateTime) {
325
- return new Stream.Transform({
326
- readableObjectMode: true,
327
- writableObjectMode: true,
328
- decodeStrings: false,
329
- highWaterMark: (type === "audio" ? 19200 : 65536),
330
- transform (chunk: Buffer | string, encoding, callback) {
331
- const timeZero = getTimeZero()
332
- const start = DateTime.now().diff(timeZero)
333
- let end = start
334
- if (type === "audio") {
335
- const duration = audioBufferDuration(chunk as Buffer)
336
- end = start.plus(duration * 1000)
337
- }
338
- const obj = new SpeechFlowChunk(start, end, "final", type, chunk)
339
- this.push(obj)
340
- callback()
341
- },
342
- final (callback) {
343
- this.push(null)
344
- callback()
345
- }
346
- })
347
- }
348
-
349
- /* ensure a chunk is of a certain type and format */
350
- export function ensureStreamChunk (type: "audio" | "text", chunk: SpeechFlowChunk | Buffer | string) {
351
- if (chunk instanceof SpeechFlowChunk) {
352
- if (chunk.type !== type)
353
- throw new Error(`invalid payload chunk (expected ${type} type, received ${chunk.type} type)`)
354
- }
355
- else {
356
- if (type === "text" && Buffer.isBuffer(chunk))
357
- chunk = chunk.toString("utf8")
358
- else if (type === "audio" && !Buffer.isBuffer(chunk))
359
- chunk = Buffer.from(chunk)
360
- }
361
- return chunk
362
- }
363
-
364
- /* type of a serialized SpeechFlow chunk */
365
- type SpeechFlowChunkSerialized = {
366
- timestampStart: number,
367
- timestampEnd: number,
368
- kind: string,
369
- type: string,
370
- payload: Uint8Array
371
- }
372
-
373
- /* encode/serialize chunk of data */
374
- export function streamChunkEncode (chunk: SpeechFlowChunk) {
375
- let payload: Uint8Array
376
- if (Buffer.isBuffer(chunk.payload))
377
- payload = new Uint8Array(chunk.payload)
378
- else {
379
- const encoder = new TextEncoder()
380
- payload = encoder.encode(chunk.payload)
381
- }
382
- const data = {
383
- timestampStart: chunk.timestampStart.toMillis(),
384
- timestampEnd: chunk.timestampEnd.toMillis(),
385
- kind: chunk.kind,
386
- type: chunk.type,
387
- payload
388
- } satisfies SpeechFlowChunkSerialized
389
- const _data = CBOR.encode(data)
390
- return _data
391
- }
392
-
393
- /* decode/unserialize chunk of data */
394
- export function streamChunkDecode (_data: Uint8Array) {
395
- let data: SpeechFlowChunkSerialized
396
- try {
397
- data = CBOR.decode<SpeechFlowChunkSerialized>(_data)
398
- }
399
- catch (err: any) {
400
- throw new Error(`CBOR decoding failed: ${err}`)
401
- }
402
- let payload: Buffer | string
403
- if (data.type === "audio")
404
- payload = Buffer.from(data.payload)
405
- else
406
- payload = (new TextDecoder()).decode(data.payload)
407
- const chunk = new SpeechFlowChunk(
408
- Duration.fromMillis(data.timestampStart),
409
- Duration.fromMillis(data.timestampEnd),
410
- data.kind as "intermediate" | "final",
411
- data.type as "audio" | "text",
412
- payload
413
- )
414
- return chunk
415
- }
416
-
417
- /* helper class for single item queue */
418
- export class SingleQueue<T> extends EventEmitter {
419
- private queue = new Array<T>()
420
- write (item: T) {
421
- this.queue.unshift(item)
422
- this.emit("dequeue")
423
- }
424
- read () {
425
- return new Promise<T>((resolve, reject) => {
426
- const consume = () =>
427
- this.queue.length > 0 ? this.queue.pop()! : null
428
- const tryToConsume = () => {
429
- const item = consume()
430
- if (item !== null)
431
- resolve(item)
432
- else
433
- this.once("dequeue", tryToConsume)
434
- }
435
- tryToConsume()
436
- })
437
- }
438
- }
439
-
440
- /* helper class for double-item queue */
441
- export class DoubleQueue<T0, T1> extends EventEmitter {
442
- private queue0 = new Array<T0>()
443
- private queue1 = new Array<T1>()
444
- private notify () {
445
- if (this.queue0.length > 0 && this.queue1.length > 0)
446
- this.emit("dequeue")
447
- }
448
- write0 (item: T0) {
449
- this.queue0.unshift(item)
450
- this.notify()
451
- }
452
- write1 (item: T1) {
453
- this.queue1.unshift(item)
454
- this.notify()
455
- }
456
- read () {
457
- return new Promise<[ T0, T1 ]>((resolve, reject) => {
458
- const consume = (): [ T0, T1 ] | null => {
459
- if (this.queue0.length > 0 && this.queue1.length > 0) {
460
- const item0 = this.queue0.pop() as T0
461
- const item1 = this.queue1.pop() as T1
462
- return [ item0, item1 ]
463
- }
464
- return null
465
- }
466
- const tryToConsume = () => {
467
- const items = consume()
468
- if (items !== null)
469
- resolve(items)
470
- else
471
- this.once("dequeue", tryToConsume)
472
- }
473
- tryToConsume()
474
- })
475
- }
476
- }
477
-
478
- /* queue element */
479
- export type QueueElement = { type: string }
480
-
481
- /* queue pointer */
482
- export class QueuePointer<T extends QueueElement> extends EventEmitter {
483
- /* internal state */
484
- private index = 0
485
-
486
- /* construction */
487
- constructor (
488
- private name: string,
489
- private queue: Queue<T>
490
- ) {
491
- super()
492
- this.setMaxListeners(100)
493
- }
494
-
495
- /* positioning operations */
496
- maxPosition () {
497
- return this.queue.elements.length
498
- }
499
- position (index?: number): number {
500
- if (index !== undefined) {
501
- this.index = Math.max(0, Math.min(index, this.queue.elements.length))
502
- this.emit("position", this.index)
503
- }
504
- return this.index
505
- }
506
- walk (num: number) {
507
- const indexOld = this.index
508
- if (num > 0)
509
- this.index = Math.min(this.index + num, this.queue.elements.length)
510
- else if (num < 0)
511
- this.index = Math.max(this.index + num, 0)
512
- if (this.index !== indexOld)
513
- this.emit("position", { start: this.index })
514
- }
515
- walkForwardUntil (type: T["type"]) {
516
- while (this.index < this.queue.elements.length
517
- && this.queue.elements[this.index].type !== type)
518
- this.index++
519
- this.emit("position", { start: this.index })
520
- }
521
- walkBackwardUntil (type: T["type"]) {
522
- while (this.index > 0
523
- && this.queue.elements[this.index].type !== type)
524
- this.index--
525
- this.emit("position", { start: this.index })
526
- }
527
-
528
- /* search operations */
529
- searchForward (type: T["type"]) {
530
- let position = this.index
531
- while (position < this.queue.elements.length
532
- && this.queue.elements[position].type !== type)
533
- position++
534
- this.emit("search", { start: this.index, end: position })
535
- return position
536
- }
537
- searchBackward (type: T["type"]) {
538
- let position = this.index
539
- while (position > 0
540
- && this.queue.elements[position].type !== type)
541
- position--
542
- this.emit("search", { start: position, end: this.index })
543
- return position
544
- }
545
-
546
- /* reading operations */
547
- peek (position?: number) {
548
- if (position === undefined)
549
- position = this.index
550
- position = Math.max(0, Math.min(position, this.queue.elements.length))
551
- const element = this.queue.elements[position]
552
- this.queue.emit("read", { start: position, end: position })
553
- return element
554
- }
555
- read () {
556
- const element = this.queue.elements[this.index]
557
- if (this.index < this.queue.elements.length)
558
- this.index++
559
- this.queue.emit("read", { start: this.index - 1, end: this.index - 1 })
560
- return element
561
- }
562
- slice (size?: number) {
563
- let slice: T[]
564
- const start = this.index
565
- if (size !== undefined) {
566
- size = Math.max(0, Math.min(size, this.queue.elements.length - this.index))
567
- slice = this.queue.elements.slice(this.index, this.index + size)
568
- this.index += size
569
- }
570
- else {
571
- slice = this.queue.elements.slice(this.index)
572
- this.index = this.queue.elements.length
573
- }
574
- this.queue.emit("read", { start, end: this.index })
575
- return slice
576
- }
577
-
578
- /* writing operations */
579
- touch () {
580
- if (this.index >= this.queue.elements.length)
581
- throw new Error("cannot touch after last element")
582
- this.queue.emit("write", { start: this.index, end: this.index + 1 })
583
- }
584
- append (element: T) {
585
- this.queue.elements.push(element)
586
- this.index = this.queue.elements.length
587
- this.queue.emit("write", { start: this.index - 1, end: this.index - 1 })
588
- }
589
- insert (element: T) {
590
- this.queue.elements.splice(this.index, 0, element)
591
- this.queue.emit("write", { start: this.index - 1, end: this.index })
592
- }
593
- delete () {
594
- if (this.index >= this.queue.elements.length)
595
- throw new Error("cannot delete after last element")
596
- this.queue.elements.splice(this.index, 1)
597
- this.queue.emit("write", { start: this.index, end: this.index })
598
- }
599
- }
600
-
601
- /* queue */
602
- export class Queue<T extends QueueElement> extends EventEmitter {
603
- public elements: T[] = []
604
- private pointers = new Map<string, QueuePointer<T>>()
605
- constructor () {
606
- super()
607
- this.setMaxListeners(100)
608
- }
609
- pointerUse (name: string): QueuePointer<T> {
610
- if (!this.pointers.has(name))
611
- this.pointers.set(name, new QueuePointer<T>(name, this))
612
- return this.pointers.get(name)!
613
- }
614
- pointerDelete (name: string): void {
615
- if (!this.pointers.has(name))
616
- throw new Error("pointer not exists")
617
- this.pointers.delete(name)
618
- }
619
- trim (): void {
620
- /* determine minimum pointer position */
621
- let min = this.elements.length
622
- for (const pointer of this.pointers.values())
623
- if (min > pointer.position())
624
- min = pointer.position()
625
-
626
- /* trim the maximum amount of first elements */
627
- if (min > 0) {
628
- this.elements.splice(0, min)
629
-
630
- /* shift all pointers */
631
- for (const pointer of this.pointers.values())
632
- pointer.position(pointer.position() - min)
633
- }
634
- }
635
- }
636
-
637
- /* utility class for wrapping a custom stream into a regular Transform stream */
638
- export class StreamWrapper extends Stream.Transform {
639
- private foreignStream: any
640
- private onData = (chunk: any) => { this.push(chunk) }
641
- private onError = (err: Error) => { this.emit("error", err) }
642
- private onEnd = () => { this.push(null) }
643
- constructor (foreignStream: any, options: Stream.TransformOptions = {}) {
644
- options.readableObjectMode = true
645
- options.writableObjectMode = true
646
- super(options)
647
- this.foreignStream = foreignStream
648
- if (typeof this.foreignStream.on === "function") {
649
- this.foreignStream.on("data", this.onData)
650
- this.foreignStream.on("error", this.onError)
651
- this.foreignStream.on("end", this.onEnd)
652
- }
653
- }
654
- _transform (chunk: any, encoding: BufferEncoding, callback: Stream.TransformCallback): void {
655
- if (this.destroyed) {
656
- callback(new Error("stream already destroyed"))
657
- return
658
- }
659
- try {
660
- if (typeof this.foreignStream.write === "function") {
661
- const canContinue = this.foreignStream.write(chunk)
662
- if (canContinue)
663
- callback()
664
- else
665
- this.foreignStream.once("drain", callback)
666
- }
667
- else
668
- throw new Error("foreign stream lacks write method")
669
- }
670
- catch (err) {
671
- callback(err as Error)
672
- }
673
- }
674
- _flush (callback: Stream.TransformCallback): void {
675
- if (this.destroyed) {
676
- callback(new Error("stream already destroyed"))
677
- return
678
- }
679
- try {
680
- if (typeof this.foreignStream.end === "function")
681
- this.foreignStream.end()
682
- callback()
683
- }
684
- catch (err) {
685
- callback(err as Error)
686
- }
687
- }
688
- _destroy (error: Error | null, callback: Stream.TransformCallback): void {
689
- if (typeof this.foreignStream.removeListener === "function") {
690
- this.foreignStream.removeListener("data", this.onData)
691
- this.foreignStream.removeListener("error", this.onError)
692
- this.foreignStream.removeListener("end", this.onEnd)
693
- }
694
- super._destroy(error, callback)
695
- }
696
- }
697
-
698
- /* meta store */
699
- interface TimeStoreInterval<T> extends IntervalTree.Interval {
700
- item: T
701
- }
702
- export class TimeStore<T> extends EventEmitter {
703
- private tree = new IntervalTree.IntervalTree<TimeStoreInterval<T>>()
704
- store (start: Duration, end: Duration, item: T): void {
705
- this.tree.insert({ low: start.toMillis(), high: end.toMillis(), item })
706
- }
707
- fetch (start: Duration, end: Duration): T[] {
708
- const intervals = this.tree.search(start.toMillis(), end.toMillis())
709
- return intervals.map((interval) => interval.item)
710
- }
711
- prune (_before: Duration): void {
712
- const before = _before.toMillis()
713
- const intervals = this.tree.search(0, before - 1)
714
- for (const interval of intervals)
715
- if (interval.low < before && interval.high < before)
716
- this.tree.remove(interval)
717
- }
718
- clear (): void {
719
- this.tree = new IntervalTree.IntervalTree<TimeStoreInterval<T>>()
720
- }
721
- }
722
-
723
- /* asynchronous queue */
724
- export class AsyncQueue<T> {
725
- private queue: Array<T | null> = []
726
- private resolvers: ((v: T | null) => void)[] = []
727
- write (v: T | null) {
728
- const resolve = this.resolvers.shift()
729
- if (resolve)
730
- resolve(v)
731
- else
732
- this.queue.push(v)
733
- }
734
- async read () {
735
- if (this.queue.length > 0)
736
- return this.queue.shift()!
737
- else
738
- return new Promise<T | null>((resolve) => this.resolvers.push(resolve))
739
- }
740
- destroy () {
741
- for (const resolve of this.resolvers)
742
- resolve(null)
743
- this.resolvers = []
744
- this.queue = []
745
- }
746
- }
747
-
748
- /* process Int16Array in fixed-size segments */
749
- export async function processInt16ArrayInSegments (
750
- data: Int16Array<ArrayBuffer>,
751
- segmentSize: number,
752
- processor: (segment: Int16Array<ArrayBuffer>) => Promise<Int16Array<ArrayBuffer>>
753
- ): Promise<Int16Array<ArrayBuffer>> {
754
- /* process full segments */
755
- let i = 0
756
- while ((i + segmentSize) <= data.length) {
757
- const segment = data.slice(i, i + segmentSize)
758
- const result = await processor(segment)
759
- data.set(result, i)
760
- i += segmentSize
761
- }
762
-
763
- /* process final partial segment if it exists */
764
- if (i < data.length) {
765
- const len = data.length - i
766
- const segment = new Int16Array(segmentSize)
767
- segment.set(data.slice(i), 0)
768
- segment.fill(0, len, segmentSize)
769
- const result = await processor(segment)
770
- data.set(result.slice(0, len), i)
771
- }
772
- return data
773
- }
774
-
775
- /* cached regular expression class */
776
- export class CachedRegExp {
777
- private cache = new Map<string, RegExp>()
778
- compile (pattern: string): RegExp | null {
779
- if (this.cache.has(pattern))
780
- return this.cache.get(pattern)!
781
- try {
782
- const regex = new RegExp(pattern)
783
- this.cache.set(pattern, regex)
784
- return regex
785
- }
786
- catch (_error) {
787
- return null
788
- }
789
- }
790
- clear (): void {
791
- this.cache.clear()
792
- }
793
- size (): number {
794
- return this.cache.size
795
- }
796
- }
797
-
798
- /* helper functions for linear/decibel conversions */
799
- export function lin2dB (x: number): number {
800
- return 20 * Math.log10(Math.max(x, 1e-12))
801
- }
802
- export function dB2lin (db: number): number {
803
- return Math.pow(10, db / 20)
804
- }