echogarden 0.11.12 → 0.11.13
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/data/schemas/options.json +16 -0
- package/dist/api/Alignment.js +2 -2
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/Recognition.js +2 -2
- package/dist/api/Recognition.js.map +1 -1
- package/dist/api/Synthesis.js +5 -4
- package/dist/api/Synthesis.js.map +1 -1
- package/dist/api/Translation.js +2 -2
- package/dist/api/Translation.js.map +1 -1
- package/dist/audio/AudioUtilities.d.ts +1 -0
- package/dist/audio/AudioUtilities.js +25 -7
- package/dist/audio/AudioUtilities.js.map +1 -1
- package/dist/cli/CLI.js +2 -2
- package/dist/cli/CLI.js.map +1 -1
- package/dist/recognition/WhisperSTT.js +2 -2
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/subtitles/Subtitles.d.ts +10 -7
- package/dist/subtitles/Subtitles.js +268 -207
- package/dist/subtitles/Subtitles.js.map +1 -1
- package/docs/Options.md +4 -2
- package/package.json +7 -6
- package/src/alignment/DTWMfccSequenceAlignment.ts +43 -0
- package/src/alignment/DTWSequenceAlignment.ts +121 -0
- package/src/alignment/DTWSequenceAlignmentWindowed.ts +210 -0
- package/src/alignment/LevenshteinSequenceAlignment.ts +126 -0
- package/src/alignment/SpeechAlignment.ts +488 -0
- package/src/api/API.ts +12 -0
- package/src/api/APIOptions.ts +15 -0
- package/src/api/Alignment.ts +329 -0
- package/src/api/Common.ts +16 -0
- package/src/api/Denoising.ts +120 -0
- package/src/api/LanguageDetection.ts +286 -0
- package/src/api/Recognition.ts +344 -0
- package/src/api/Synthesis.ts +1735 -0
- package/src/api/Translation.ts +143 -0
- package/src/api/Vad.ts +172 -0
- package/src/audio/AudioBufferConversion.ts +248 -0
- package/src/audio/AudioPlayer.ts +358 -0
- package/src/audio/AudioRecorder.ts +91 -0
- package/src/audio/AudioUtilities.ts +392 -0
- package/src/audio/SoxPath.ts +24 -0
- package/src/cli/CLI.ts +1360 -0
- package/src/cli/CLIConfigFile.ts +91 -0
- package/src/cli/CLILauncher.ts +26 -0
- package/src/cli/CLIOptionsSchema.ts +54 -0
- package/src/cli/CLIParser.ts +41 -0
- package/src/cli/CLIStarter.ts +40 -0
- package/src/codecs/FFMpegTranscoder.ts +214 -0
- package/src/codecs/TIMITCodec.ts +17 -0
- package/src/codecs/WaveCodec.ts +260 -0
- package/src/denoising/RNNoise.ts +95 -0
- package/src/dsp/BiquadFilter.ts +488 -0
- package/src/dsp/FFT.ts +187 -0
- package/src/dsp/MFCC.ts +227 -0
- package/src/dsp/MelSpectogram.ts +145 -0
- package/src/dsp/Rubberband.ts +249 -0
- package/src/dsp/Sonic.ts +59 -0
- package/src/dsp/SpeexResampler.ts +79 -0
- package/src/math/VectorMath.ts +812 -0
- package/src/nlp/ChineseSegmentation.ts +68 -0
- package/src/nlp/CompromiseNLP.ts +113 -0
- package/src/nlp/EspeakPhonemizer.ts +168 -0
- package/src/nlp/IPA.ts +139 -0
- package/src/nlp/JapaneseSegmentation.ts +53 -0
- package/src/nlp/Lexicon.ts +119 -0
- package/src/nlp/PhoneConversion.ts +508 -0
- package/src/nlp/Segmentation.ts +237 -0
- package/src/nlp/TextNormalizer.ts +160 -0
- package/src/recognition/AmazonTranscribeSTT.ts +112 -0
- package/src/recognition/AzureCognitiveServicesSTT.ts +76 -0
- package/src/recognition/GoogleCloudSTT.ts +92 -0
- package/src/recognition/SileroSTT.ts +173 -0
- package/src/recognition/VoskSTT.ts +112 -0
- package/src/recognition/WhisperSTT.ts +1518 -0
- package/src/server/Client.ts +297 -0
- package/src/server/Server.ts +178 -0
- package/src/server/ServerStarter.ts +12 -0
- package/src/server/Worker.ts +400 -0
- package/src/server/WorkerStarter.ts +38 -0
- package/src/speech-language-detection/SileroLanguageDetection.ts +105 -0
- package/src/subtitles/Subtitles.ts +478 -0
- package/src/synthesis/AwsPollyTTS.ts +78 -0
- package/src/synthesis/AzureCognitiveServicesTTS.ts +146 -0
- package/src/synthesis/CoquiServerTTS.ts +29 -0
- package/src/synthesis/ElevenLabsTTS.ts +104 -0
- package/src/synthesis/EspeakTTS.ts +552 -0
- package/src/synthesis/FliteTTS.ts +387 -0
- package/src/synthesis/GoogleCloudTTS.ts +112 -0
- package/src/synthesis/GoogleTranslateTTS.ts +210 -0
- package/src/synthesis/MicrosoftEdgeTTS.ts +298 -0
- package/src/synthesis/SamTTS.ts +30 -0
- package/src/synthesis/SapiTTS.ts +222 -0
- package/src/synthesis/StreamlabsPollyTTS.ts +114 -0
- package/src/synthesis/SvoxPicoTTS.ts +318 -0
- package/src/synthesis/VitsTTS.ts +734 -0
- package/src/tests/Test.ts +24 -0
- package/src/text-language-detection/FastTextLanguageDetection.ts +53 -0
- package/src/text-language-detection/TinyLDLanguageDetection.ts +16 -0
- package/src/typings/Fillers.d.ts +41 -0
- package/src/utilities/BinaryArrayConversion.ts +159 -0
- package/src/utilities/Compression.ts +91 -0
- package/src/utilities/FileDownloader.ts +201 -0
- package/src/utilities/FileSystem.ts +265 -0
- package/src/utilities/Hashing.ts +230 -0
- package/src/utilities/Locale.ts +119 -0
- package/src/utilities/Logger.ts +72 -0
- package/src/utilities/NdArrayUtilities.ts +31 -0
- package/src/utilities/ObjectUtilities.ts +169 -0
- package/src/utilities/OpenPromise.ts +13 -0
- package/src/utilities/PackageManager.ts +97 -0
- package/src/utilities/Queue.ts +17 -0
- package/src/utilities/RandomGenerator.ts +237 -0
- package/src/utilities/SignalChannel.ts +22 -0
- package/src/utilities/TarballMaker.ts +68 -0
- package/src/utilities/Timeline.ts +231 -0
- package/src/utilities/Timer.ts +93 -0
- package/src/utilities/Utilities.ts +574 -0
- package/src/utilities/WasmMemoryManager.ts +516 -0
- package/src/utilities/WebReader.ts +55 -0
- package/src/utilities/WikipediaReader.ts +41 -0
- package/src/voice-activity-detection/SileroVAD.ts +86 -0
- package/src/voice-activity-detection/WebRtcVAD.ts +76 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { ParagraphBreakType, WhitespaceProcessing } from "../api/Common.js"
|
|
2
|
+
import { isWordOrSymbolWord, splitToParagraphs, splitToSentences } from "../nlp/Segmentation.js"
|
|
3
|
+
import { deepClone } from "./ObjectUtilities.js"
|
|
4
|
+
import { getUTF32Chars, roundToDigits } from "./Utilities.js"
|
|
5
|
+
|
|
6
|
+
export function addTimeOffsetToTimeline(targetTimeline: Timeline, timeOffset: number) {
|
|
7
|
+
if (!targetTimeline) {
|
|
8
|
+
return targetTimeline
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const newTimeline = deepClone(targetTimeline)
|
|
12
|
+
|
|
13
|
+
for (const segmentTimelineEntry of newTimeline) {
|
|
14
|
+
segmentTimelineEntry.startTime = Math.max(segmentTimelineEntry.startTime + timeOffset, 0)
|
|
15
|
+
segmentTimelineEntry.endTime = Math.max(segmentTimelineEntry.endTime + timeOffset, 0)
|
|
16
|
+
|
|
17
|
+
if (segmentTimelineEntry.timeline) {
|
|
18
|
+
segmentTimelineEntry.timeline = addTimeOffsetToTimeline(segmentTimelineEntry.timeline, timeOffset)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return newTimeline
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function multiplyTimelineByFactor(targetTimeline: Timeline, factor: number) {
|
|
26
|
+
const newTimeline = deepClone(targetTimeline)
|
|
27
|
+
|
|
28
|
+
for (const segmentTimelineEntry of newTimeline) {
|
|
29
|
+
segmentTimelineEntry.startTime = segmentTimelineEntry.startTime * factor
|
|
30
|
+
segmentTimelineEntry.endTime = segmentTimelineEntry.endTime * factor
|
|
31
|
+
|
|
32
|
+
if (segmentTimelineEntry.timeline) {
|
|
33
|
+
segmentTimelineEntry.timeline = multiplyTimelineByFactor(segmentTimelineEntry.timeline, factor)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return newTimeline
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function roundTimelineProperties(targetTimeline: Timeline, decimalDigits = 2) {
|
|
41
|
+
const roundedTimeline = deepClone(targetTimeline)
|
|
42
|
+
|
|
43
|
+
for (const entry of roundedTimeline) {
|
|
44
|
+
if (entry.startTime) {
|
|
45
|
+
entry.startTime = roundToDigits(entry.startTime, decimalDigits)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (entry.endTime) {
|
|
49
|
+
entry.endTime = roundToDigits(entry.endTime, decimalDigits)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (entry.confidence) {
|
|
53
|
+
entry.confidence = roundToDigits(entry.confidence, decimalDigits)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (entry.timeline) {
|
|
57
|
+
entry.timeline = roundTimelineProperties(entry.timeline)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return roundedTimeline
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function wordTimelineToSegmentSentenceTimeline(wordTimeline: Timeline, transcript: string, language: string, paragraphBreaks: ParagraphBreakType = 'double', whitespace: WhitespaceProcessing = 'collapse') {
|
|
65
|
+
const paragraphs = await splitToParagraphs(transcript, paragraphBreaks, whitespace)
|
|
66
|
+
|
|
67
|
+
const segments = paragraphs
|
|
68
|
+
.map(segment =>
|
|
69
|
+
splitToSentences(segment, language).map(sentence =>
|
|
70
|
+
sentence.trim()))
|
|
71
|
+
|
|
72
|
+
let text = ""
|
|
73
|
+
const charIndexToSentenceEntryMapping: TimelineEntry[] = []
|
|
74
|
+
|
|
75
|
+
const segmentTimeline: Timeline = []
|
|
76
|
+
|
|
77
|
+
for (const segment of segments) {
|
|
78
|
+
const sentencesInSegment: Timeline = []
|
|
79
|
+
|
|
80
|
+
const segmentEntry: TimelineEntry = {
|
|
81
|
+
type: "segment",
|
|
82
|
+
text: "",
|
|
83
|
+
startTime: -1,
|
|
84
|
+
endTime: -1,
|
|
85
|
+
timeline: sentencesInSegment
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const sentence of segment) {
|
|
89
|
+
const sentenceEntry: TimelineEntry = {
|
|
90
|
+
type: "sentence",
|
|
91
|
+
text: sentence,
|
|
92
|
+
startTime: -1,
|
|
93
|
+
endTime: -1,
|
|
94
|
+
timeline: []
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const char of sentence + " ") {
|
|
98
|
+
text += char
|
|
99
|
+
charIndexToSentenceEntryMapping.push(sentenceEntry)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
sentencesInSegment.push(sentenceEntry)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
segmentTimeline.push(segmentEntry)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let wordSearchStartOffset = 0
|
|
109
|
+
|
|
110
|
+
for (let wordIndex = 0; wordIndex < wordTimeline.length; wordIndex++) {
|
|
111
|
+
const wordEntry = wordTimeline[wordIndex]
|
|
112
|
+
const wordText = wordEntry.text
|
|
113
|
+
|
|
114
|
+
if (!isWordOrSymbolWord(wordText)) {
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const indexOfWordInText = text.indexOf(wordText, wordSearchStartOffset)
|
|
119
|
+
|
|
120
|
+
if (indexOfWordInText == -1) {
|
|
121
|
+
throw new Error(`Couldn't find the word '${wordText}' in the text at start position ${wordSearchStartOffset}`)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const targetSentenceEntry = charIndexToSentenceEntryMapping[indexOfWordInText]
|
|
125
|
+
targetSentenceEntry.timeline!.push(wordEntry)
|
|
126
|
+
|
|
127
|
+
wordSearchStartOffset = indexOfWordInText + wordText.length
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const newSegmentTimeline: Timeline = []
|
|
131
|
+
|
|
132
|
+
for (const segmentEntry of segmentTimeline) {
|
|
133
|
+
const oldSentenceTimeline = segmentEntry.timeline!
|
|
134
|
+
|
|
135
|
+
const newSentenceTimeline: Timeline = []
|
|
136
|
+
|
|
137
|
+
for (const sentenceEntry of oldSentenceTimeline) {
|
|
138
|
+
const wordTimeline = sentenceEntry.timeline
|
|
139
|
+
|
|
140
|
+
if (!wordTimeline || wordTimeline.length == 0) {
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
sentenceEntry.startTime = wordTimeline[0].startTime
|
|
145
|
+
sentenceEntry.endTime = wordTimeline[wordTimeline.length - 1].endTime
|
|
146
|
+
|
|
147
|
+
newSentenceTimeline.push(sentenceEntry)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (newSentenceTimeline.length == 0) {
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
segmentEntry.text = newSentenceTimeline.map(sentenceEntry => sentenceEntry.text).join(" ")
|
|
155
|
+
|
|
156
|
+
segmentEntry.startTime = newSentenceTimeline[0].startTime
|
|
157
|
+
segmentEntry.endTime = newSentenceTimeline[newSentenceTimeline.length - 1].endTime
|
|
158
|
+
|
|
159
|
+
newSegmentTimeline.push(segmentEntry)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return { segmentTimeline: newSegmentTimeline }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function addWordTextOffsetsToTimeline(timeline: Timeline, text: string, currentOffset = 0) {
|
|
166
|
+
const { mapping } = getUTF32Chars(text)
|
|
167
|
+
|
|
168
|
+
for (const entry of timeline) {
|
|
169
|
+
if (entry.type == 'word') {
|
|
170
|
+
let word = entry.text
|
|
171
|
+
|
|
172
|
+
word = word.trim().replaceAll(/\s+/g, ' ')
|
|
173
|
+
|
|
174
|
+
const wordParts = word.split(' ')
|
|
175
|
+
|
|
176
|
+
let startOffset: number | undefined
|
|
177
|
+
let endOffset: number | undefined
|
|
178
|
+
|
|
179
|
+
for (let i = 0; i < wordParts.length; i++) {
|
|
180
|
+
let wordPart = wordParts[i]
|
|
181
|
+
|
|
182
|
+
let wordPartOffset = text.indexOf(wordPart, currentOffset)
|
|
183
|
+
|
|
184
|
+
if (wordPartOffset == -1) {
|
|
185
|
+
continue
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
currentOffset = wordPartOffset + wordParts[i].length
|
|
189
|
+
|
|
190
|
+
if (i == 0) {
|
|
191
|
+
startOffset = wordPartOffset
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
endOffset = currentOffset
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
entry.startOffsetUtf16 = startOffset
|
|
198
|
+
entry.endOffsetUtf16 = endOffset
|
|
199
|
+
|
|
200
|
+
entry.startOffsetUtf32 = startOffset != undefined ? mapping[startOffset] : undefined
|
|
201
|
+
entry.endOffsetUtf32 = endOffset != undefined ? mapping[endOffset] : undefined
|
|
202
|
+
} else if (entry.timeline) {
|
|
203
|
+
currentOffset = addWordTextOffsetsToTimeline(entry.timeline, text, currentOffset)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return currentOffset
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export type TimelineEntryType = "segment" | "paragraph" | "sentence" | "clause" | "phrase" | "word" | "token" | "letter" | "phone" | "subphone"
|
|
211
|
+
|
|
212
|
+
export type TimelineEntry = {
|
|
213
|
+
type: TimelineEntryType
|
|
214
|
+
|
|
215
|
+
text: string,
|
|
216
|
+
|
|
217
|
+
startTime: number,
|
|
218
|
+
endTime: number,
|
|
219
|
+
|
|
220
|
+
startOffsetUtf16?: number
|
|
221
|
+
endOffsetUtf16?: number
|
|
222
|
+
|
|
223
|
+
startOffsetUtf32?: number
|
|
224
|
+
endOffsetUtf32?: number
|
|
225
|
+
|
|
226
|
+
confidence?: number
|
|
227
|
+
|
|
228
|
+
timeline?: Timeline
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export type Timeline = TimelineEntry[]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { roundToDigits, writeToStderr } from "./Utilities.js"
|
|
2
|
+
|
|
3
|
+
declare const chrome: any
|
|
4
|
+
declare const process: any
|
|
5
|
+
|
|
6
|
+
export class Timer {
|
|
7
|
+
startTime = 0
|
|
8
|
+
|
|
9
|
+
constructor() {
|
|
10
|
+
this.restart()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
restart() {
|
|
14
|
+
this.startTime = Timer.currentTime
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get elapsedTime(): number {
|
|
18
|
+
// Elapsed time (milliseconds)
|
|
19
|
+
return Timer.currentTime - this.startTime
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get elapsedTimeSeconds(): number {
|
|
23
|
+
// Elapsed time (seconds)
|
|
24
|
+
return this.elapsedTime / 1000
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getElapsedTimeAndRestart(): number {
|
|
28
|
+
const elapsedTime = this.elapsedTime
|
|
29
|
+
this.restart()
|
|
30
|
+
return elapsedTime
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
logAndRestart(title: string, timePrecision = 3): number {
|
|
34
|
+
const elapsedTime = this.elapsedTime
|
|
35
|
+
|
|
36
|
+
//
|
|
37
|
+
const message = `${title}: ${roundToDigits(elapsedTime, timePrecision)}ms`
|
|
38
|
+
writeToStderr(message)
|
|
39
|
+
//
|
|
40
|
+
|
|
41
|
+
this.restart()
|
|
42
|
+
|
|
43
|
+
return elapsedTime
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static get currentTime(): number {
|
|
47
|
+
if (!this.timestampFunc) {
|
|
48
|
+
this.createGlobalTimestampFunction()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return this.timestampFunc()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static get microsecondTimestamp(): number {
|
|
55
|
+
return Math.floor(Timer.currentTime * 1000)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private static createGlobalTimestampFunction() {
|
|
59
|
+
if (typeof process === "object" && typeof process.hrtime === "function") {
|
|
60
|
+
let baseTimestamp = 0
|
|
61
|
+
|
|
62
|
+
this.timestampFunc = () => {
|
|
63
|
+
const nodeTimeStamp = process.hrtime()
|
|
64
|
+
const millisecondTime = (nodeTimeStamp[0] * 1000) + (nodeTimeStamp[1] / 1000000)
|
|
65
|
+
|
|
66
|
+
return baseTimestamp + millisecondTime
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
baseTimestamp = Date.now() - this.timestampFunc()
|
|
70
|
+
}
|
|
71
|
+
else if (typeof chrome === "object" && chrome.Interval) {
|
|
72
|
+
const baseTimestamp = Date.now()
|
|
73
|
+
|
|
74
|
+
const chromeIntervalObject = new chrome.Interval()
|
|
75
|
+
chromeIntervalObject.start()
|
|
76
|
+
|
|
77
|
+
this.timestampFunc = () => baseTimestamp + chromeIntervalObject.microseconds() / 1000
|
|
78
|
+
}
|
|
79
|
+
else if (typeof performance === "object" && performance.now) {
|
|
80
|
+
const baseTimestamp = Date.now() - performance.now()
|
|
81
|
+
|
|
82
|
+
this.timestampFunc = () => baseTimestamp + performance.now()
|
|
83
|
+
}
|
|
84
|
+
else if (Date.now) {
|
|
85
|
+
this.timestampFunc = () => Date.now()
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.timestampFunc = () => (new Date()).getTime()
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private static timestampFunc: () => number
|
|
93
|
+
}
|