speechflow 1.6.3 → 1.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -0
- package/CHANGELOG.md +20 -0
- package/README.md +87 -48
- package/etc/speechflow.yaml +21 -14
- package/package.json +5 -5
- package/speechflow-cli/dst/speechflow-main-api.js +3 -7
- package/speechflow-cli/dst/speechflow-main-api.js.map +1 -1
- package/speechflow-cli/dst/speechflow-main-graph.js +1 -1
- package/speechflow-cli/dst/speechflow-main.js +6 -0
- package/speechflow-cli/dst/speechflow-main.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js +1 -21
- package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.js +2 -1
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js +1 -21
- package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-expander.js +2 -1
- package/speechflow-cli/dst/speechflow-node-a2a-expander.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js +2 -2
- package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gender.js +38 -42
- package/speechflow-cli/dst/speechflow-node-a2a-gender.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-meter.js +59 -40
- package/speechflow-cli/dst/speechflow-node-a2a-meter.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-vad.js +2 -2
- package/speechflow-cli/dst/speechflow-node-a2a-vad.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-openai.d.ts +0 -1
- package/speechflow-cli/dst/speechflow-node-a2t-openai.js +0 -6
- package/speechflow-cli/dst/speechflow-node-a2t-openai.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.d.ts +0 -1
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.js +0 -6
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.d.ts +0 -1
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js +0 -6
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.d.ts +0 -1
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js +0 -6
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-ollama.js +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-ollama.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js +6 -6
- package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-x2x-trace.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-node-x2x-trace.js +22 -2
- package/speechflow-cli/dst/speechflow-node-x2x-trace.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-device.js +3 -2
- package/speechflow-cli/dst/speechflow-node-xio-device.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-websocket.js.map +1 -1
- package/speechflow-cli/dst/speechflow-util-audio.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-util-audio.js +21 -0
- package/speechflow-cli/dst/speechflow-util-audio.js.map +1 -1
- package/speechflow-cli/dst/speechflow-util-error.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-util-error.js +7 -1
- package/speechflow-cli/dst/speechflow-util-error.js.map +1 -1
- package/speechflow-cli/dst/speechflow-util-stream.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-util-stream.js +2 -2
- package/speechflow-cli/dst/speechflow-util-stream.js.map +1 -1
- package/speechflow-cli/etc/oxlint.jsonc +2 -1
- package/speechflow-cli/etc/stx.conf +6 -10
- package/speechflow-cli/package.json +19 -19
- package/speechflow-cli/src/speechflow-main-api.ts +6 -13
- package/speechflow-cli/src/speechflow-main-graph.ts +1 -1
- package/speechflow-cli/src/speechflow-main.ts +4 -0
- package/speechflow-cli/src/speechflow-node-a2a-compressor-wt.ts +1 -29
- package/speechflow-cli/src/speechflow-node-a2a-compressor.ts +2 -1
- package/speechflow-cli/src/speechflow-node-a2a-expander-wt.ts +1 -29
- package/speechflow-cli/src/speechflow-node-a2a-expander.ts +2 -1
- package/speechflow-cli/src/speechflow-node-a2a-ffmpeg.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-gender.ts +44 -39
- package/speechflow-cli/src/speechflow-node-a2a-meter.ts +67 -44
- package/speechflow-cli/src/speechflow-node-a2a-rnnoise.ts +1 -0
- package/speechflow-cli/src/speechflow-node-a2a-vad.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2t-openai.ts +0 -6
- package/speechflow-cli/src/speechflow-node-t2a-amazon.ts +0 -6
- package/speechflow-cli/src/speechflow-node-t2a-elevenlabs.ts +0 -6
- package/speechflow-cli/src/speechflow-node-t2a-kokoro.ts +0 -6
- package/speechflow-cli/src/speechflow-node-t2t-ollama.ts +1 -1
- package/speechflow-cli/src/speechflow-node-t2t-subtitle.ts +10 -14
- package/speechflow-cli/src/speechflow-node-x2x-trace.ts +25 -2
- package/speechflow-cli/src/speechflow-node-xio-device.ts +3 -2
- package/speechflow-cli/src/speechflow-node-xio-websocket.ts +1 -1
- package/speechflow-cli/src/speechflow-util-audio.ts +30 -0
- package/speechflow-cli/src/speechflow-util-error.ts +9 -3
- package/speechflow-cli/src/speechflow-util-stream.ts +2 -2
- package/speechflow-ui-db/dst/index.js +24 -33
- package/speechflow-ui-db/package.json +14 -12
- package/speechflow-ui-db/src/app.vue +30 -7
- package/speechflow-ui-st/.claude/settings.local.json +3 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-BoldIt.eot +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-BoldIt.ttf +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-BoldIt.woff +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-RegularIt.eot +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-RegularIt.ttf +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-RegularIt.woff +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-Semibold.eot +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-Semibold.ttf +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-Semibold.woff +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-SemiboldIt.eot +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-SemiboldIt.ttf +0 -0
- package/speechflow-ui-st/dst/app-font-TypoPRO-SourceSansPro-SemiboldIt.woff +0 -0
- package/speechflow-ui-st/dst/index.css +2 -2
- package/speechflow-ui-st/dst/index.js +461 -28
- package/speechflow-ui-st/package.json +14 -13
- package/speechflow-ui-st/src/app.vue +150 -51
- package/speechflow-ui-st/src/index.ts +4 -0
- package/speechflow-cli/dst/speechflow-util-webaudio-wt.d.ts +0 -1
- package/speechflow-cli/dst/speechflow-util-webaudio-wt.js +0 -124
- package/speechflow-cli/dst/speechflow-util-webaudio-wt.js.map +0 -1
- package/speechflow-cli/dst/speechflow-util-webaudio.d.ts +0 -13
- package/speechflow-cli/dst/speechflow-util-webaudio.js +0 -137
- package/speechflow-cli/dst/speechflow-util-webaudio.js.map +0 -1
|
@@ -15,21 +15,21 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"vue": "3.5.21",
|
|
18
|
-
"vue3-spinners": "1.3.
|
|
19
|
-
"moment": "2.30.1",
|
|
18
|
+
"vue3-spinners": "1.3.3",
|
|
20
19
|
"luxon": "3.7.2",
|
|
21
20
|
"@opensumi/reconnecting-websocket": "4.4.0",
|
|
22
|
-
"axios": "1.
|
|
21
|
+
"axios": "1.12.2",
|
|
23
22
|
"typopro-web": "4.2.7",
|
|
24
23
|
"@fortawesome/fontawesome-free": "7.0.1",
|
|
25
24
|
"patch-package": "8.0.0",
|
|
26
|
-
"@rse/stx": "1.1.
|
|
25
|
+
"@rse/stx": "1.1.2",
|
|
26
|
+
"animejs": "4.1.3"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"vite": "7.1.
|
|
30
|
-
"typescript-eslint": "8.
|
|
31
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
32
|
-
"@typescript-eslint/parser": "8.
|
|
29
|
+
"vite": "7.1.6",
|
|
30
|
+
"typescript-eslint": "8.44.0",
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "8.44.0",
|
|
32
|
+
"@typescript-eslint/parser": "8.44.0",
|
|
33
33
|
"@vitejs/plugin-vue": "6.0.1",
|
|
34
34
|
"@rollup/plugin-yaml": "4.1.2",
|
|
35
35
|
"vite-plugin-node-polyfills": "0.24.0",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
|
|
40
40
|
"@vue/eslint-config-typescript": "14.6.0",
|
|
41
41
|
"vue-eslint-parser": "10.2.0",
|
|
42
|
-
"eslint": "9.
|
|
43
|
-
"@eslint/js": "9.
|
|
42
|
+
"eslint": "9.36.0",
|
|
43
|
+
"@eslint/js": "9.36.0",
|
|
44
44
|
"neostandard": "0.12.2",
|
|
45
45
|
"eslint-plugin-import": "2.32.0",
|
|
46
46
|
"eslint-plugin-vue": "10.4.0",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
"nodemon": "3.1.10",
|
|
52
52
|
"shx": "0.4.0",
|
|
53
|
-
"stylelint": "16.
|
|
53
|
+
"stylelint": "16.24.0",
|
|
54
54
|
"stylelint-config-html": "1.1.0",
|
|
55
55
|
"stylelint-config-recommended-vue": "1.6.1",
|
|
56
56
|
"stylelint-config-standard": "39.0.0",
|
|
@@ -59,12 +59,13 @@
|
|
|
59
59
|
"postcss-html": "1.8.0",
|
|
60
60
|
"stylus": "0.64.0",
|
|
61
61
|
"typescript": "5.9.2",
|
|
62
|
-
"vue-tsc": "3.0.
|
|
62
|
+
"vue-tsc": "3.0.7",
|
|
63
63
|
"delay-cli": "2.0.0",
|
|
64
64
|
"cross-env": "10.0.0",
|
|
65
65
|
"serve": "14.2.5",
|
|
66
66
|
|
|
67
|
-
"@types/luxon": "3.7.1"
|
|
67
|
+
"@types/luxon": "3.7.1",
|
|
68
|
+
"@types/animejs": "3.1.13"
|
|
68
69
|
},
|
|
69
70
|
"overrides": {
|
|
70
71
|
"@liuli-util/vite-plugin-node": { "vite": ">=6.0.0" }
|
|
@@ -8,23 +8,25 @@
|
|
|
8
8
|
|
|
9
9
|
<template>
|
|
10
10
|
<div class="app">
|
|
11
|
-
<div class="
|
|
12
|
-
<div class="
|
|
13
|
-
v-bind:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class="
|
|
17
|
-
{
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
<div class="area">
|
|
12
|
+
<div class="block" ref="block">
|
|
13
|
+
<span v-bind:key="chunk.id"
|
|
14
|
+
v-for="(chunk, idx) of text"
|
|
15
|
+
class="chunk"
|
|
16
|
+
v-bind:class="{ intermediate: chunk.kind === 'intermediate', removed: chunk.removed }"
|
|
17
|
+
v-bind:ref="`chunk-${chunk.id}`">
|
|
18
|
+
{{ chunk.text }}
|
|
19
|
+
<span class="cursor" v-if="idx === (text.length - 1) && chunk.kind === 'intermediate'">
|
|
20
|
+
<spinner-grid class="spinner-grid" size="50"/>
|
|
20
21
|
</span>
|
|
21
|
-
</
|
|
22
|
+
</span>
|
|
22
23
|
</div>
|
|
23
24
|
</div>
|
|
24
25
|
</div>
|
|
25
26
|
</template>
|
|
26
27
|
|
|
27
28
|
<style lang="stylus">
|
|
29
|
+
/* entire app */
|
|
28
30
|
.app
|
|
29
31
|
position: relative
|
|
30
32
|
width: 100vw
|
|
@@ -35,53 +37,74 @@
|
|
|
35
37
|
flex-direction: column
|
|
36
38
|
justify-content: center
|
|
37
39
|
align-items: center
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
|
|
41
|
+
/* rendering area */
|
|
42
|
+
.area
|
|
43
|
+
height: auto
|
|
44
|
+
min-height: 30vh
|
|
45
|
+
max-height: 60vh
|
|
46
|
+
width: 75vw
|
|
41
47
|
position: absolute
|
|
42
|
-
bottom:
|
|
43
|
-
|
|
48
|
+
bottom: 4vh
|
|
49
|
+
mask: linear-gradient(to bottom, transparent 0%, black 50%)
|
|
50
|
+
overflow-x: hidden
|
|
51
|
+
overflow-y: hidden
|
|
52
|
+
display: flex
|
|
53
|
+
flex-direction: column
|
|
54
|
+
align-items: center
|
|
55
|
+
justify-content: flex-end
|
|
56
|
+
|
|
57
|
+
/* content block */
|
|
58
|
+
.block
|
|
59
|
+
display: block
|
|
60
|
+
text-align: left
|
|
44
61
|
width: 100%
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
padding: 0.5vw 1.0vw
|
|
59
|
-
margin-bottom: 0.5vw
|
|
60
|
-
overflow-wrap: break-word
|
|
62
|
+
overflow-wrap: break-word
|
|
63
|
+
|
|
64
|
+
/* content chunk */
|
|
65
|
+
.chunk
|
|
66
|
+
color: #eeeeee
|
|
67
|
+
background-color: #000000b0
|
|
68
|
+
text-shadow: 0 0 0.20vw #000000
|
|
69
|
+
-webkit-text-stroke: 0.05vw #000000
|
|
70
|
+
font-size: 2.0vw
|
|
71
|
+
font-weight: 600
|
|
72
|
+
line-height: 1.4
|
|
73
|
+
padding-left: 0.20vw
|
|
74
|
+
padding-right: 0.20vw
|
|
61
75
|
.cursor
|
|
62
76
|
display: inline-block
|
|
63
77
|
margin-left: 10px
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
78
|
+
&.intermediate:last-child
|
|
79
|
+
color: #ffffff
|
|
80
|
+
&.removed
|
|
81
|
+
opacity: 0
|
|
67
82
|
</style>
|
|
68
83
|
|
|
69
84
|
<script setup lang="ts">
|
|
70
|
-
import { defineComponent }
|
|
71
|
-
import { VueSpinnerGrid }
|
|
72
|
-
import
|
|
73
|
-
import
|
|
74
|
-
import
|
|
85
|
+
import { defineComponent } from "vue"
|
|
86
|
+
import { VueSpinnerGrid } from "vue3-spinners"
|
|
87
|
+
import { DateTime, Duration } from "luxon"
|
|
88
|
+
import ReconnectingWebSocket from "@opensumi/reconnecting-websocket"
|
|
89
|
+
import * as anime from "animejs"
|
|
75
90
|
</script>
|
|
76
91
|
|
|
77
92
|
<script lang="ts">
|
|
93
|
+
type TextChunk = {
|
|
94
|
+
id: string,
|
|
95
|
+
timestamp: DateTime,
|
|
96
|
+
kind: "intermediate" | "final",
|
|
97
|
+
text: string,
|
|
98
|
+
removing: boolean,
|
|
99
|
+
removed: boolean
|
|
100
|
+
}
|
|
78
101
|
type SpeechFlowChunk = {
|
|
79
102
|
timestampStart: Duration,
|
|
80
103
|
timestampEnd: Duration,
|
|
81
104
|
kind: "intermediate" | "final",
|
|
82
105
|
type: "text",
|
|
83
106
|
payload: string,
|
|
84
|
-
meta: Map<string,
|
|
107
|
+
meta: Map<string, unknown>
|
|
85
108
|
}
|
|
86
109
|
export default defineComponent({
|
|
87
110
|
name: "app",
|
|
@@ -89,22 +112,64 @@ export default defineComponent({
|
|
|
89
112
|
"spinner-grid": VueSpinnerGrid
|
|
90
113
|
},
|
|
91
114
|
data: () => ({
|
|
92
|
-
text: [] as
|
|
93
|
-
|
|
115
|
+
text: [] as TextChunk[],
|
|
116
|
+
chunkIdCounter: 0,
|
|
117
|
+
cleanupIntervalId: null as ReturnType<typeof setInterval> | null
|
|
94
118
|
}),
|
|
95
119
|
async mounted () {
|
|
96
|
-
/* determine API
|
|
97
|
-
const
|
|
120
|
+
/* determine API URLs */
|
|
121
|
+
const urlHTTP = new URL("/api", document.location.href)
|
|
122
|
+
const urlWS = new URL("/api", document.location.href)
|
|
123
|
+
urlWS.protocol = (urlHTTP.protocol === "https:" ? "wss:" : "ws:")
|
|
124
|
+
|
|
125
|
+
/* optically remove outdated text chunks */
|
|
126
|
+
this.cleanupIntervalId = setInterval(() => {
|
|
127
|
+
for (const chunk of this.text) {
|
|
128
|
+
if (chunk.timestamp < DateTime.now().minus({ seconds: 20 }) && !chunk.removing && !chunk.removed) {
|
|
129
|
+
const el = this.$refs[`chunk-${chunk.id}`] as HTMLSpanElement
|
|
130
|
+
if (!el)
|
|
131
|
+
continue
|
|
132
|
+
|
|
133
|
+
/* start removing */
|
|
134
|
+
chunk.removing = true
|
|
135
|
+
chunk.removed = false
|
|
136
|
+
anime.animate(el, {
|
|
137
|
+
opacity: [ 1, 0 ],
|
|
138
|
+
duration: 2000,
|
|
139
|
+
easing: "easeOutQuad",
|
|
140
|
+
onComplete: () => {
|
|
141
|
+
/* end removing */
|
|
142
|
+
chunk.removing = false
|
|
143
|
+
chunk.removed = true
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}, 500)
|
|
98
149
|
|
|
99
150
|
/* connect to WebSocket API for receiving dashboard information */
|
|
100
|
-
const ws = new ReconnectingWebSocket(
|
|
151
|
+
const ws = new ReconnectingWebSocket(urlWS.toString(), [], {
|
|
101
152
|
reconnectionDelayGrowFactor: 1.3,
|
|
102
153
|
maxReconnectionDelay: 4000,
|
|
103
154
|
minReconnectionDelay: 1000,
|
|
104
155
|
connectionTimeout: 4000,
|
|
105
156
|
minUptime: 5000
|
|
106
157
|
})
|
|
158
|
+
ws.addEventListener("error", (ev) => {
|
|
159
|
+
this.log("ERROR", `WebSocket error: ${ev.message}`)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
/* track connection open/close */
|
|
163
|
+
ws.addEventListener("open", (ev) => {
|
|
164
|
+
this.log("INFO", "WebSocket connection established")
|
|
165
|
+
})
|
|
166
|
+
ws.addEventListener("close", (ev) => {
|
|
167
|
+
this.log("INFO", "WebSocket connection destroyed")
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
/* receive messages */
|
|
107
171
|
ws.addEventListener("message", (ev) => {
|
|
172
|
+
/* parse message */
|
|
108
173
|
let chunk: SpeechFlowChunk
|
|
109
174
|
try {
|
|
110
175
|
chunk = JSON.parse(ev.data)
|
|
@@ -113,18 +178,52 @@ export default defineComponent({
|
|
|
113
178
|
this.log("ERROR", "Failed to parse WebSocket message", { error, data: ev.data })
|
|
114
179
|
return
|
|
115
180
|
}
|
|
116
|
-
|
|
117
|
-
|
|
181
|
+
|
|
182
|
+
/* process text chunks */
|
|
183
|
+
if (this.text.length > 0 && this.text[this.text.length - 1].kind === "intermediate") {
|
|
184
|
+
/* override previous intermediate text chunk
|
|
185
|
+
with either another intermediate one or a final one */
|
|
186
|
+
const lastChunk = this.text[this.text.length - 1]
|
|
187
|
+
lastChunk.text = chunk.payload
|
|
188
|
+
lastChunk.kind = chunk.kind
|
|
189
|
+
lastChunk.timestamp = DateTime.now()
|
|
190
|
+
}
|
|
118
191
|
else {
|
|
119
|
-
|
|
120
|
-
|
|
192
|
+
/* remove content in case all chunks were removed
|
|
193
|
+
(this way new next chunk starts at the left edge again
|
|
194
|
+
and the overall memory consumption is reduced) */
|
|
195
|
+
if (this.text.every((chunk) => chunk.removed))
|
|
196
|
+
this.text = []
|
|
197
|
+
|
|
198
|
+
/* add new text chunk */
|
|
199
|
+
this.text.push({
|
|
200
|
+
id: `chunk-${this.chunkIdCounter++}`,
|
|
201
|
+
text: chunk.payload,
|
|
202
|
+
kind: chunk.kind,
|
|
203
|
+
timestamp: DateTime.now(),
|
|
204
|
+
removing: false,
|
|
205
|
+
removed: false
|
|
206
|
+
})
|
|
121
207
|
}
|
|
122
|
-
|
|
208
|
+
|
|
209
|
+
/* ensure we always scrolled the chunks inside the block to the bottom */
|
|
210
|
+
this.$nextTick(() => {
|
|
211
|
+
const block = this.$refs.block as HTMLDivElement
|
|
212
|
+
block.scrollTop = block.scrollHeight
|
|
213
|
+
})
|
|
123
214
|
})
|
|
124
215
|
},
|
|
216
|
+
beforeUnmount () {
|
|
217
|
+
/* cleanup */
|
|
218
|
+
if (this.cleanupIntervalId !== null) {
|
|
219
|
+
clearInterval(this.cleanupIntervalId)
|
|
220
|
+
this.cleanupIntervalId = null
|
|
221
|
+
}
|
|
222
|
+
},
|
|
125
223
|
methods: {
|
|
224
|
+
/* helper function for console logging */
|
|
126
225
|
log (level: string, msg: string, data: { [ key: string ]: any } | null = null) {
|
|
127
|
-
const timestamp =
|
|
226
|
+
const timestamp = DateTime.now().toFormat("yyyy-MM-dd HH:mm:ss.SSS")
|
|
128
227
|
let output = `${timestamp} [${level}]: ${msg}`
|
|
129
228
|
if (data !== null)
|
|
130
229
|
output += ` (${Object.keys(data)
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
/* external dependencies */
|
|
8
8
|
import * as Vue from "vue"
|
|
9
9
|
import "typopro-web/web/TypoPRO-SourceSansPro/TypoPRO-SourceSansPro-Regular.css"
|
|
10
|
+
import "typopro-web/web/TypoPRO-SourceSansPro/TypoPRO-SourceSansPro-RegularIt.css"
|
|
11
|
+
import "typopro-web/web/TypoPRO-SourceSansPro/TypoPRO-SourceSansPro-Semibold.css"
|
|
12
|
+
import "typopro-web/web/TypoPRO-SourceSansPro/TypoPRO-SourceSansPro-SemiboldIt.css"
|
|
10
13
|
import "typopro-web/web/TypoPRO-SourceSansPro/TypoPRO-SourceSansPro-Bold.css"
|
|
14
|
+
import "typopro-web/web/TypoPRO-SourceSansPro/TypoPRO-SourceSansPro-BoldIt.css"
|
|
11
15
|
import "@fortawesome/fontawesome-free/js/all.min.js"
|
|
12
16
|
import "@fortawesome/fontawesome-free/css/all.min.css"
|
|
13
17
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
** SpeechFlow - Speech Processing Flow Graph
|
|
4
|
-
** Copyright (c) 2024-2025 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
5
|
-
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
6
|
-
*/
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
/* audio source node */
|
|
9
|
-
class AudioSourceProcessor extends AudioWorkletProcessor {
|
|
10
|
-
/* internal state */
|
|
11
|
-
pendingData = [];
|
|
12
|
-
currentChunk = null;
|
|
13
|
-
currentOffset = 0;
|
|
14
|
-
/* node construction */
|
|
15
|
-
constructor() {
|
|
16
|
-
super();
|
|
17
|
-
/* receive input chunks */
|
|
18
|
-
this.port.addEventListener("message", (event) => {
|
|
19
|
-
const { type, chunkId } = event.data;
|
|
20
|
-
if (type === "input-chunk")
|
|
21
|
-
this.pendingData.push({ data: event.data.data.pcmData, chunkId });
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
/* process audio frame */
|
|
25
|
-
process(inputs, /* unused */ outputs, parameters /* unused */) {
|
|
26
|
-
/* determine output */
|
|
27
|
-
const output = outputs[0];
|
|
28
|
-
if (!output || output.length === 0)
|
|
29
|
-
return true;
|
|
30
|
-
const frameCount = output[0].length;
|
|
31
|
-
const channelCount = output.length;
|
|
32
|
-
/* get current chunk if we don't have one */
|
|
33
|
-
if (this.currentChunk === null && this.pendingData.length > 0) {
|
|
34
|
-
this.currentChunk = this.pendingData.shift();
|
|
35
|
-
this.currentOffset = 0;
|
|
36
|
-
/* signal chunk start */
|
|
37
|
-
const message = {
|
|
38
|
-
type: "chunk-started",
|
|
39
|
-
chunkId: this.currentChunk.chunkId
|
|
40
|
-
};
|
|
41
|
-
this.port.postMessage(message);
|
|
42
|
-
}
|
|
43
|
-
/* process input */
|
|
44
|
-
if (this.currentChunk) {
|
|
45
|
-
/* output current chunk */
|
|
46
|
-
const samplesPerChannel = this.currentChunk.data.length / channelCount;
|
|
47
|
-
const remainingFrames = samplesPerChannel - this.currentOffset;
|
|
48
|
-
const framesToProcess = Math.min(frameCount, remainingFrames);
|
|
49
|
-
/* copy data from current chunk (interleaved to planar) */
|
|
50
|
-
for (let frame = 0; frame < framesToProcess; frame++) {
|
|
51
|
-
for (let ch = 0; ch < channelCount; ch++) {
|
|
52
|
-
const interleavedIndex = (this.currentOffset + frame) * channelCount + ch;
|
|
53
|
-
output[ch][frame] = this.currentChunk.data[interleavedIndex] ?? 0;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/* zero-pad remaining output if needed */
|
|
57
|
-
for (let frame = framesToProcess; frame < frameCount; frame++)
|
|
58
|
-
for (let ch = 0; ch < channelCount; ch++)
|
|
59
|
-
output[ch][frame] = 0;
|
|
60
|
-
/* check if current chunk is finished */
|
|
61
|
-
this.currentOffset += framesToProcess;
|
|
62
|
-
if (this.currentOffset >= samplesPerChannel) {
|
|
63
|
-
this.currentChunk = null;
|
|
64
|
-
this.currentOffset = 0;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
/* output silence when no input */
|
|
69
|
-
for (let ch = 0; ch < channelCount; ch++)
|
|
70
|
-
output[ch].fill(0);
|
|
71
|
-
}
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
/* audio capture node */
|
|
76
|
-
class AudioCaptureProcessor extends AudioWorkletProcessor {
|
|
77
|
-
/* internal state */
|
|
78
|
-
activeCaptures = new Map();
|
|
79
|
-
/* node construction */
|
|
80
|
-
constructor() {
|
|
81
|
-
super();
|
|
82
|
-
/* receive start of capturing command */
|
|
83
|
-
this.port.addEventListener("message", (event) => {
|
|
84
|
-
const { type, chunkId } = event.data;
|
|
85
|
-
if (type === "start-capture") {
|
|
86
|
-
this.activeCaptures.set(chunkId, {
|
|
87
|
-
data: [],
|
|
88
|
-
expectedSamples: event.data.expectedSamples
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
/* process audio frame */
|
|
94
|
-
process(inputs, outputs, /* unused */ parameters /* unused */) {
|
|
95
|
-
/* determine input */
|
|
96
|
-
const input = inputs[0];
|
|
97
|
-
if (!input || input.length === 0 || this.activeCaptures.size === 0)
|
|
98
|
-
return true;
|
|
99
|
-
const frameCount = input[0].length;
|
|
100
|
-
const channelCount = input.length;
|
|
101
|
-
/* iterate over all active captures */
|
|
102
|
-
for (const [chunkId, capture] of this.activeCaptures) {
|
|
103
|
-
/* convert planar to interleaved */
|
|
104
|
-
for (let frame = 0; frame < frameCount; frame++)
|
|
105
|
-
for (let ch = 0; ch < channelCount; ch++)
|
|
106
|
-
capture.data.push(input[ch][frame]);
|
|
107
|
-
/* send back captured data */
|
|
108
|
-
if (capture.data.length >= capture.expectedSamples) {
|
|
109
|
-
const message = {
|
|
110
|
-
type: "capture-complete",
|
|
111
|
-
chunkId,
|
|
112
|
-
data: capture.data.slice(0, capture.expectedSamples)
|
|
113
|
-
};
|
|
114
|
-
this.port.postMessage(message);
|
|
115
|
-
this.activeCaptures.delete(chunkId);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return true;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
/* register the new audio nodes */
|
|
122
|
-
registerProcessor("source", AudioSourceProcessor);
|
|
123
|
-
registerProcessor("capture", AudioCaptureProcessor);
|
|
124
|
-
//# sourceMappingURL=speechflow-util-webaudio-wt.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"speechflow-util-webaudio-wt.js","sourceRoot":"","sources":["../src/speechflow-util-webaudio-wt.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { AudioContext, AudioWorkletNode } from "node-web-audio-api";
|
|
2
|
-
export declare class WebAudio {
|
|
3
|
-
sampleRate: number;
|
|
4
|
-
channels: number;
|
|
5
|
-
audioContext: AudioContext;
|
|
6
|
-
sourceNode: AudioWorkletNode | null;
|
|
7
|
-
captureNode: AudioWorkletNode | null;
|
|
8
|
-
private pendingPromises;
|
|
9
|
-
constructor(sampleRate: number, channels: number);
|
|
10
|
-
setup(): Promise<void>;
|
|
11
|
-
process(int16Array: Int16Array): Promise<Int16Array>;
|
|
12
|
-
destroy(): Promise<void>;
|
|
13
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
** SpeechFlow - Speech Processing Flow Graph
|
|
4
|
-
** Copyright (c) 2024-2025 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
5
|
-
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
6
|
-
*/
|
|
7
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
-
};
|
|
10
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.WebAudio = void 0;
|
|
12
|
-
/* standard dependencies */
|
|
13
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
-
/* external dependencies */
|
|
15
|
-
const node_web_audio_api_1 = require("node-web-audio-api");
|
|
16
|
-
class WebAudio {
|
|
17
|
-
sampleRate;
|
|
18
|
-
channels;
|
|
19
|
-
/* internal state */
|
|
20
|
-
audioContext;
|
|
21
|
-
sourceNode = null;
|
|
22
|
-
captureNode = null;
|
|
23
|
-
pendingPromises = new Map();
|
|
24
|
-
/* construct object */
|
|
25
|
-
constructor(sampleRate, channels) {
|
|
26
|
-
this.sampleRate = sampleRate;
|
|
27
|
-
this.channels = channels;
|
|
28
|
-
/* create new audio context */
|
|
29
|
-
this.audioContext = new node_web_audio_api_1.AudioContext({
|
|
30
|
-
sampleRate,
|
|
31
|
-
latencyHint: "interactive"
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
/* setup object */
|
|
35
|
-
async setup() {
|
|
36
|
-
/* ensure audio context is not suspended */
|
|
37
|
-
if (this.audioContext.state === "suspended")
|
|
38
|
-
await this.audioContext.resume();
|
|
39
|
-
/* add audio worklet module */
|
|
40
|
-
const url = node_path_1.default.resolve(__dirname, "speechflow-util-webaudio-wt.js");
|
|
41
|
-
await this.audioContext.audioWorklet.addModule(url);
|
|
42
|
-
/* create source node */
|
|
43
|
-
this.sourceNode = new node_web_audio_api_1.AudioWorkletNode(this.audioContext, "source", {
|
|
44
|
-
numberOfInputs: 0,
|
|
45
|
-
numberOfOutputs: 1,
|
|
46
|
-
outputChannelCount: [this.channels]
|
|
47
|
-
});
|
|
48
|
-
/* create capture node */
|
|
49
|
-
this.captureNode = new node_web_audio_api_1.AudioWorkletNode(this.audioContext, "capture", {
|
|
50
|
-
numberOfInputs: 1,
|
|
51
|
-
numberOfOutputs: 0
|
|
52
|
-
});
|
|
53
|
-
this.captureNode.port.addEventListener("message", (event) => {
|
|
54
|
-
const { type, chunkId, data } = event.data ?? {};
|
|
55
|
-
if (type === "capture-complete") {
|
|
56
|
-
const promise = this.pendingPromises.get(chunkId);
|
|
57
|
-
if (promise) {
|
|
58
|
-
clearTimeout(promise.timeout);
|
|
59
|
-
this.pendingPromises.delete(chunkId);
|
|
60
|
-
const int16Data = new Int16Array(data.length);
|
|
61
|
-
for (let i = 0; i < data.length; i++)
|
|
62
|
-
int16Data[i] = Math.max(-32768, Math.min(32767, Math.round(data[i] * 32767)));
|
|
63
|
-
promise.resolve(int16Data);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
/* start ports */
|
|
68
|
-
this.sourceNode.port.start();
|
|
69
|
-
this.captureNode.port.start();
|
|
70
|
-
}
|
|
71
|
-
/* process single audio chunk */
|
|
72
|
-
async process(int16Array) {
|
|
73
|
-
const chunkId = `chunk_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
74
|
-
return new Promise((resolve, reject) => {
|
|
75
|
-
const timeout = setTimeout(() => {
|
|
76
|
-
this.pendingPromises.delete(chunkId);
|
|
77
|
-
reject(new Error("processing timeout"));
|
|
78
|
-
}, (int16Array.length / this.audioContext.sampleRate) * 1000 + 250);
|
|
79
|
-
if (this.captureNode !== null)
|
|
80
|
-
this.pendingPromises.set(chunkId, { resolve, reject, timeout });
|
|
81
|
-
try {
|
|
82
|
-
const float32Data = new Float32Array(int16Array.length);
|
|
83
|
-
for (let i = 0; i < int16Array.length; i++)
|
|
84
|
-
float32Data[i] = int16Array[i] / 32768.0;
|
|
85
|
-
/* start capture first */
|
|
86
|
-
if (this.captureNode !== null) {
|
|
87
|
-
this.captureNode?.port.postMessage({
|
|
88
|
-
type: "start-capture",
|
|
89
|
-
chunkId,
|
|
90
|
-
expectedSamples: int16Array.length
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
/* small delay to ensure capture is ready before sending data */
|
|
94
|
-
setTimeout(() => {
|
|
95
|
-
/* send input to source node */
|
|
96
|
-
this.sourceNode?.port.postMessage({
|
|
97
|
-
type: "input-chunk",
|
|
98
|
-
chunkId,
|
|
99
|
-
data: { pcmData: float32Data, channels: this.channels }
|
|
100
|
-
}, [float32Data.buffer]);
|
|
101
|
-
}, 5);
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
clearTimeout(timeout);
|
|
105
|
-
if (this.captureNode !== null)
|
|
106
|
-
this.pendingPromises.delete(chunkId);
|
|
107
|
-
reject(new Error(`failed to process chunk: ${error}`));
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
async destroy() {
|
|
112
|
-
/* reject all pending promises */
|
|
113
|
-
try {
|
|
114
|
-
this.pendingPromises.forEach(({ reject, timeout }) => {
|
|
115
|
-
clearTimeout(timeout);
|
|
116
|
-
reject(new Error("WebAudio destroyed"));
|
|
117
|
-
});
|
|
118
|
-
this.pendingPromises.clear();
|
|
119
|
-
}
|
|
120
|
-
catch (_err) {
|
|
121
|
-
/* ignored - cleanup during shutdown */
|
|
122
|
-
}
|
|
123
|
-
/* disconnect nodes */
|
|
124
|
-
if (this.sourceNode !== null) {
|
|
125
|
-
this.sourceNode.disconnect();
|
|
126
|
-
this.sourceNode = null;
|
|
127
|
-
}
|
|
128
|
-
if (this.captureNode !== null) {
|
|
129
|
-
this.captureNode.disconnect();
|
|
130
|
-
this.captureNode = null;
|
|
131
|
-
}
|
|
132
|
-
/* stop context */
|
|
133
|
-
await this.audioContext.close();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
exports.WebAudio = WebAudio;
|
|
137
|
-
//# sourceMappingURL=speechflow-util-webaudio.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"speechflow-util-webaudio.js","sourceRoot":"","sources":["../src/speechflow-util-webaudio.ts"],"names":[],"mappings":"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"}
|