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,400 @@
|
|
|
1
|
+
import { RequestVoiceListResult, SynthesisOptions, SynthesisSegmentEventData, SynthesisResult, VoiceListRequestOptions, requestVoiceList, synthesize } from "../api/Synthesis.js"
|
|
2
|
+
import { Queue } from "../utilities/Queue.js"
|
|
3
|
+
import { logToStderr, yieldToEventLoop } from "../utilities/Utilities.js"
|
|
4
|
+
import { AudioSourceParam } from "../audio/AudioUtilities.js"
|
|
5
|
+
import { RecognitionOptions, RecognitionResult, recognize } from "../api/Recognition.js"
|
|
6
|
+
import { AlignmentOptions, AlignmentResult, align } from "../api/Alignment.js"
|
|
7
|
+
import { SpeechTranslationOptions, SpeechTranslationResult, translateSpeech } from "../api/Translation.js"
|
|
8
|
+
import { Logger, resetActiveLogger } from "../utilities/Logger.js"
|
|
9
|
+
import { writeToStderr } from '../utilities/Utilities.js'
|
|
10
|
+
import { resolveToModuleRootDir } from '../utilities/FileSystem.js'
|
|
11
|
+
import { Worker, SHARE_ENV } from 'node:worker_threads'
|
|
12
|
+
import { SpeechLanguageDetectionOptions, SpeechLanguageDetectionResult, TextLanguageDetectionOptions, TextLanguageDetectionResult, detectSpeechLanguage, detectTextLanguage } from "../api/LanguageDetection.js"
|
|
13
|
+
import chalk from "chalk"
|
|
14
|
+
|
|
15
|
+
const log = logToStderr
|
|
16
|
+
|
|
17
|
+
const messageChannel = new MessageChannel()
|
|
18
|
+
messageChannel.port1.start()
|
|
19
|
+
messageChannel.port2.start()
|
|
20
|
+
|
|
21
|
+
const canceledRequests = new Set<string>()
|
|
22
|
+
let cancelCurrentTask = false
|
|
23
|
+
|
|
24
|
+
export function shouldCancelCurrentTask() {
|
|
25
|
+
return cancelCurrentTask
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
addListenerToClientMessages((message) => {
|
|
29
|
+
if (message.messageType == "CancelationRequest") {
|
|
30
|
+
//log(`CANCEL REQUESTED FOR ${message.requestId}`)
|
|
31
|
+
canceledRequests.add(message.requestId)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
enqueueAndProcessIfIdle(message)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const messageQueue = new Queue<any>()
|
|
39
|
+
let isProcessing = false
|
|
40
|
+
|
|
41
|
+
function enqueueAndProcessIfIdle(message: any) {
|
|
42
|
+
messageQueue.enqueue(message)
|
|
43
|
+
processQueueIfIdle()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function processQueueIfIdle() {
|
|
47
|
+
if (isProcessing) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
isProcessing = true
|
|
52
|
+
|
|
53
|
+
while (!messageQueue.isEmpty) {
|
|
54
|
+
const incomingMessage = messageQueue.dequeue()
|
|
55
|
+
const requestId = incomingMessage.requestId
|
|
56
|
+
|
|
57
|
+
function sendMessage(outgoingMessage: any) {
|
|
58
|
+
sendMessageToClient({
|
|
59
|
+
requestId,
|
|
60
|
+
...outgoingMessage,
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function setCancelationFlagIfNeeded() {
|
|
65
|
+
if (canceledRequests.has(requestId)) {
|
|
66
|
+
cancelCurrentTask = true
|
|
67
|
+
canceledRequests.delete(requestId)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await yieldToEventLoop()
|
|
72
|
+
|
|
73
|
+
setCancelationFlagIfNeeded()
|
|
74
|
+
const cancelationFlagSetterInterval = setInterval(setCancelationFlagIfNeeded, 20)
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
if (cancelCurrentTask) {
|
|
78
|
+
//log(`******* CANCELED BEFORE START: ${requestId} *******`)
|
|
79
|
+
throw new Error("Canceled")
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
await processMessage(incomingMessage, sendMessage)
|
|
83
|
+
} catch (e: any) {
|
|
84
|
+
log(`${chalk.redBright("Error")}: ${e.message}`)
|
|
85
|
+
|
|
86
|
+
sendMessageToClient({
|
|
87
|
+
requestId,
|
|
88
|
+
messageType: "Error",
|
|
89
|
+
error: e
|
|
90
|
+
})
|
|
91
|
+
} finally {
|
|
92
|
+
resetActiveLogger()
|
|
93
|
+
|
|
94
|
+
clearInterval(cancelationFlagSetterInterval)
|
|
95
|
+
cancelCurrentTask = false
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
isProcessing = false
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function processMessage(message: WorkerRequestMessage, sendMessage: MessageFunc) {
|
|
103
|
+
switch (message.messageType) {
|
|
104
|
+
case "SynthesisRequest": {
|
|
105
|
+
await processSynthesisRequest(message, sendMessage)
|
|
106
|
+
break
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
case "VoiceListRequest": {
|
|
110
|
+
await processVoiceListRequest(message, sendMessage)
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
case "RecognitionRequest": {
|
|
115
|
+
await processRecognitionRequest(message, sendMessage)
|
|
116
|
+
break
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
case "AlignmentRequest": {
|
|
120
|
+
await processAlignmentRequest(message, sendMessage)
|
|
121
|
+
break
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
case "SpeechTranslationRequest": {
|
|
125
|
+
await processSpeechTranslationRequest(message, sendMessage)
|
|
126
|
+
break
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
case "SpeechLanguageDetectionRequest": {
|
|
130
|
+
await processSpeechLanguageDetectionRequest(message, sendMessage)
|
|
131
|
+
break
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
case "TextLanguageDetectionRequest": {
|
|
135
|
+
await processTextLanguageDetectionRequest(message, sendMessage)
|
|
136
|
+
break
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
default: {
|
|
140
|
+
throw new Error(`Invalid message type: ${(message as any).messageType}`)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
146
|
+
// Synthesis operations
|
|
147
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
148
|
+
async function processSynthesisRequest(message: SynthesisRequestMessage, sendMessage: MessageFunc) {
|
|
149
|
+
async function onSegment(eventData: SynthesisSegmentEventData) {
|
|
150
|
+
const responseMessage: SynthesisSegmentEventMessage = {
|
|
151
|
+
messageType: "SynthesisSegmentEvent",
|
|
152
|
+
...eventData
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
sendMessage(responseMessage)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function onSentence(eventData: SynthesisSegmentEventData) {
|
|
159
|
+
const responseMessage: SynthesisSentenceEventMessage = {
|
|
160
|
+
messageType: "SynthesisSentenceEvent",
|
|
161
|
+
...eventData
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
sendMessage(responseMessage)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const result = await synthesize(message.input, message.options, onSegment, onSentence)
|
|
168
|
+
|
|
169
|
+
const responseMessage: SynthesisResponseMessage = {
|
|
170
|
+
messageType: "SynthesisResponse",
|
|
171
|
+
...result
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
sendMessage(responseMessage)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Synthesis message types
|
|
178
|
+
export interface SynthesisRequestMessage extends WorkerMessageBase {
|
|
179
|
+
messageType: "SynthesisRequest"
|
|
180
|
+
input: string | string[]
|
|
181
|
+
options: SynthesisOptions
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface SynthesisResponseMessage extends WorkerMessageBase, SynthesisResult {
|
|
185
|
+
messageType: "SynthesisResponse"
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface SynthesisSegmentEventMessage extends WorkerMessageBase, SynthesisSegmentEventData {
|
|
189
|
+
messageType: "SynthesisSegmentEvent"
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface SynthesisSentenceEventMessage extends WorkerMessageBase, SynthesisSegmentEventData {
|
|
193
|
+
messageType: "SynthesisSentenceEvent"
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function processVoiceListRequest(message: VoiceListRequestMessage, sendMessage: MessageFunc) {
|
|
197
|
+
const result = await requestVoiceList(message.options)
|
|
198
|
+
|
|
199
|
+
const responseMessage: VoiceListResponseMessage = {
|
|
200
|
+
messageType: "VoiceListResponse",
|
|
201
|
+
...result
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
sendMessage(responseMessage)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Voice list message types
|
|
208
|
+
export interface VoiceListRequestMessage extends WorkerMessageBase {
|
|
209
|
+
messageType: "VoiceListRequest"
|
|
210
|
+
options: VoiceListRequestOptions
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export interface VoiceListResponseMessage extends WorkerMessageBase, RequestVoiceListResult {
|
|
214
|
+
messageType: "VoiceListResponse"
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
218
|
+
// Recognition operations
|
|
219
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
220
|
+
async function processRecognitionRequest(message: RecognitionRequestMessage, sendMessage: MessageFunc) {
|
|
221
|
+
const result = await recognize(message.input, message.options)
|
|
222
|
+
|
|
223
|
+
const responseMessage: RecognitionResponseMessage = {
|
|
224
|
+
messageType: "RecognitionResponse",
|
|
225
|
+
...result
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
sendMessage(responseMessage)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Recognition message types
|
|
232
|
+
export interface RecognitionRequestMessage extends WorkerMessageBase {
|
|
233
|
+
messageType: "RecognitionRequest"
|
|
234
|
+
input: AudioSourceParam
|
|
235
|
+
options: RecognitionOptions
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface RecognitionResponseMessage extends WorkerMessageBase, RecognitionResult {
|
|
239
|
+
messageType: "RecognitionResponse"
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
243
|
+
// Alignment operations
|
|
244
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
245
|
+
async function processAlignmentRequest(message: AlignmentRequestMessage, sendMessage: MessageFunc) {
|
|
246
|
+
const result = await align(message.input, message.transcript, message.options)
|
|
247
|
+
|
|
248
|
+
const responseMessage: AlignmentResponseMessage = {
|
|
249
|
+
messageType: "AlignmentResponse",
|
|
250
|
+
...result
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
sendMessage(responseMessage)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Alignment message types
|
|
257
|
+
export interface AlignmentRequestMessage extends WorkerMessageBase {
|
|
258
|
+
messageType: "AlignmentRequest"
|
|
259
|
+
input: AudioSourceParam
|
|
260
|
+
transcript: string
|
|
261
|
+
options: AlignmentOptions
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface AlignmentResponseMessage extends WorkerMessageBase, AlignmentResult {
|
|
265
|
+
messageType: "AlignmentResponse"
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
269
|
+
// Speech translation operations
|
|
270
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
271
|
+
async function processSpeechTranslationRequest(message: SpeechTranslationRequestMessage, sendMessage: MessageFunc) {
|
|
272
|
+
const result = await translateSpeech(message.input, message.options)
|
|
273
|
+
|
|
274
|
+
const responseMessage: SpeechTranslationResponseMessage = {
|
|
275
|
+
messageType: "SpeechTranslationResponse",
|
|
276
|
+
...result
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
sendMessage(responseMessage)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Speech translation message types
|
|
283
|
+
export interface SpeechTranslationRequestMessage extends WorkerMessageBase {
|
|
284
|
+
messageType: "SpeechTranslationRequest"
|
|
285
|
+
input: AudioSourceParam
|
|
286
|
+
options: SpeechTranslationOptions
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export interface SpeechTranslationResponseMessage extends WorkerMessageBase, SpeechTranslationResult {
|
|
290
|
+
messageType: "SpeechTranslationResponse"
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
294
|
+
// Speech Language detection operations
|
|
295
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
296
|
+
async function processSpeechLanguageDetectionRequest(message: SpeechLanguageDetectionRequestMessage, sendMessage: MessageFunc) {
|
|
297
|
+
const result = await detectSpeechLanguage(message.input, message.options)
|
|
298
|
+
|
|
299
|
+
const responseMessage: SpeechLanguageDetectionResponseMessage = {
|
|
300
|
+
messageType: "SpeechLanguageDetectionResponse",
|
|
301
|
+
...result
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
sendMessage(responseMessage)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Speech language detection message types
|
|
308
|
+
export interface SpeechLanguageDetectionRequestMessage extends WorkerMessageBase {
|
|
309
|
+
messageType: "SpeechLanguageDetectionRequest"
|
|
310
|
+
input: AudioSourceParam
|
|
311
|
+
options: SpeechLanguageDetectionOptions
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export interface SpeechLanguageDetectionResponseMessage extends WorkerMessageBase, SpeechLanguageDetectionResult {
|
|
315
|
+
messageType: "SpeechLanguageDetectionResponse"
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
319
|
+
// Text Language detection operations
|
|
320
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
321
|
+
async function processTextLanguageDetectionRequest(message: TextLanguageDetectionRequestMessage, sendMessage: MessageFunc) {
|
|
322
|
+
const result = await detectTextLanguage(message.input, message.options)
|
|
323
|
+
|
|
324
|
+
const responseMessage: TextLanguageDetectionResponseMessage = {
|
|
325
|
+
messageType: "TextLanguageDetectionResponse",
|
|
326
|
+
...result
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
sendMessage(responseMessage)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Text language detection message types
|
|
333
|
+
export interface TextLanguageDetectionRequestMessage extends WorkerMessageBase {
|
|
334
|
+
messageType: "TextLanguageDetectionRequest"
|
|
335
|
+
input: string
|
|
336
|
+
options: TextLanguageDetectionOptions
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export interface TextLanguageDetectionResponseMessage extends WorkerMessageBase, TextLanguageDetectionResult {
|
|
340
|
+
messageType: "TextLanguageDetectionResponse"
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
344
|
+
// Messaging methods
|
|
345
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
346
|
+
export function sendMessageToWorker(message: any) {
|
|
347
|
+
messageChannel.port1.postMessage(message)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function addListenerToWorkerMessages(handler: MessageFunc) {
|
|
351
|
+
messageChannel.port1.addEventListener('message', (event) => {
|
|
352
|
+
handler(event.data)
|
|
353
|
+
})
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function sendMessageToClient(message: any) {
|
|
357
|
+
messageChannel.port2.postMessage(message)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function addListenerToClientMessages(handler: MessageFunc) {
|
|
361
|
+
messageChannel.port2.addEventListener('message', (event) => {
|
|
362
|
+
handler(event.data)
|
|
363
|
+
})
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
367
|
+
// Worker thread methods
|
|
368
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
369
|
+
export async function startNewWorkerThread() {
|
|
370
|
+
const workerThread = new Worker(resolveToModuleRootDir('dist/server/WorkerStarter.js'), {
|
|
371
|
+
argv: process.argv.slice(2),
|
|
372
|
+
env: SHARE_ENV
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
workerThread.on("message", (message) => {
|
|
376
|
+
if (message.name == "writeToStdErr") {
|
|
377
|
+
writeToStderr(message.text)
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
workerThread.postMessage({
|
|
382
|
+
name: 'init',
|
|
383
|
+
stdErrIsTTY: process.stderr.isTTY,
|
|
384
|
+
stdErrHasColors: process.stderr.hasColors ? process.stderr.hasColors() : false
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
return workerThread
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
391
|
+
// Base message types
|
|
392
|
+
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
393
|
+
export type WorkerRequestMessage = SynthesisRequestMessage | VoiceListRequestMessage | RecognitionRequestMessage | AlignmentRequestMessage | SpeechTranslationRequestMessage | SpeechLanguageDetectionRequestMessage | TextLanguageDetectionRequestMessage
|
|
394
|
+
|
|
395
|
+
export interface WorkerMessageBase {
|
|
396
|
+
messageType: string
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export type MessageFunc = (message: any) => void
|
|
400
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isMainThread, parentPort } from 'node:worker_threads'
|
|
2
|
+
import { sendMessageToWorker, addListenerToWorkerMessages } from './Worker.js'
|
|
3
|
+
import { OpenPromise } from '../utilities/OpenPromise.js'
|
|
4
|
+
|
|
5
|
+
async function startIfInWorkerThread() {
|
|
6
|
+
if (isMainThread || !parentPort) {
|
|
7
|
+
return
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
addListenerToWorkerMessages((message) => {
|
|
11
|
+
parentPort?.postMessage(message)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const initOpenPromise = new OpenPromise<void>()
|
|
15
|
+
|
|
16
|
+
parentPort.once("message", (message) => {
|
|
17
|
+
if (message.name == 'init') {
|
|
18
|
+
process.stderr.isTTY = message.stdErrIsTTY
|
|
19
|
+
process.stderr.hasColors = () => message.hasColors
|
|
20
|
+
|
|
21
|
+
process.stderr.write = (text) => {
|
|
22
|
+
parentPort!.postMessage({ name: 'writeToStdErr', text })
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
initOpenPromise.resolve()
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
await initOpenPromise.promise
|
|
31
|
+
|
|
32
|
+
parentPort.on("message", (message: any) => {
|
|
33
|
+
sendMessageToWorker(message)
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Start worker if running in worker thread
|
|
38
|
+
startIfInWorkerThread()
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import Onnx from 'onnxruntime-node'
|
|
2
|
+
import { softmax } from '../math/VectorMath.js'
|
|
3
|
+
import { Logger } from "../utilities/Logger.js"
|
|
4
|
+
import { RawAudio } from "../audio/AudioUtilities.js"
|
|
5
|
+
import { readAndParseJsonFile } from '../utilities/FileSystem.js'
|
|
6
|
+
import { detectSpeechLanguageByParts, type LanguageDetectionResults } from '../api/LanguageDetection.js'
|
|
7
|
+
import { languageCodeToName } from '../utilities/Locale.js'
|
|
8
|
+
|
|
9
|
+
export async function detectLanguage(rawAudio: RawAudio, modelPath: string, languageDictionaryPath: string, languageGroupDictionaryPath: string) {
|
|
10
|
+
const languageDetection = new SileroLanguageDetection(modelPath, languageDictionaryPath, languageGroupDictionaryPath)
|
|
11
|
+
await languageDetection.initialize()
|
|
12
|
+
|
|
13
|
+
async function detectLanguageForPart(partAudio: RawAudio) {
|
|
14
|
+
const { languageResults } = await languageDetection.detectLanguage(partAudio)
|
|
15
|
+
|
|
16
|
+
return languageResults
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const results = await detectSpeechLanguageByParts(rawAudio, detectLanguageForPart)
|
|
20
|
+
|
|
21
|
+
results.sort((a, b) => b.probability - a.probability)
|
|
22
|
+
|
|
23
|
+
return results
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class SileroLanguageDetection {
|
|
27
|
+
modelPath: string
|
|
28
|
+
languageDictionaryPath: string
|
|
29
|
+
languageGroupDictionaryPath: string
|
|
30
|
+
|
|
31
|
+
languageDictionary: any
|
|
32
|
+
languageGroupDictionary: any
|
|
33
|
+
|
|
34
|
+
session: Onnx.InferenceSession | undefined
|
|
35
|
+
|
|
36
|
+
constructor(modelPath: string, languageDictionaryPath: string, languageGroupDictionaryPath: string) {
|
|
37
|
+
this.modelPath = modelPath
|
|
38
|
+
this.languageDictionaryPath = languageDictionaryPath
|
|
39
|
+
this.languageGroupDictionaryPath = languageGroupDictionaryPath
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async initialize() {
|
|
43
|
+
const logger = new Logger()
|
|
44
|
+
logger.start("Initialize ONNX inference session")
|
|
45
|
+
|
|
46
|
+
this.languageDictionary = await readAndParseJsonFile(this.languageDictionaryPath)
|
|
47
|
+
this.languageGroupDictionary = await readAndParseJsonFile(this.languageGroupDictionaryPath)
|
|
48
|
+
|
|
49
|
+
const onnxOptions: Onnx.InferenceSession.SessionOptions = {
|
|
50
|
+
logSeverityLevel: 3
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.session = await Onnx.InferenceSession.create(this.modelPath, onnxOptions)
|
|
54
|
+
|
|
55
|
+
logger.end()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async detectLanguage(rawAudio: RawAudio) {
|
|
59
|
+
const logger = new Logger()
|
|
60
|
+
|
|
61
|
+
logger.start("Detect language with Silero")
|
|
62
|
+
|
|
63
|
+
const audioSamples = rawAudio.audioChannels[0]
|
|
64
|
+
|
|
65
|
+
const inputTensor = new Onnx.Tensor('float32', audioSamples, [1, audioSamples.length])
|
|
66
|
+
|
|
67
|
+
const inputs = { input: inputTensor }
|
|
68
|
+
|
|
69
|
+
const results = await this.session!.run(inputs)
|
|
70
|
+
|
|
71
|
+
logger.start("Parse model results")
|
|
72
|
+
|
|
73
|
+
const languageLogits = results["output"].data
|
|
74
|
+
const languageGroupLogits = results["2038"].data
|
|
75
|
+
|
|
76
|
+
const languageProbabilities = softmax(languageLogits as any)
|
|
77
|
+
const languageGroupProbabilities = softmax(languageGroupLogits as any)
|
|
78
|
+
|
|
79
|
+
const languageResults: LanguageDetectionResults = []
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < languageProbabilities.length; i++) {
|
|
82
|
+
const languageString = this.languageDictionary[i]
|
|
83
|
+
const languageCode = languageString.replace(/,.*$/, "")
|
|
84
|
+
|
|
85
|
+
languageResults.push({
|
|
86
|
+
language: languageCode,
|
|
87
|
+
languageName: languageCodeToName(languageCode),
|
|
88
|
+
probability: languageProbabilities[i]
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const languageGroupResults: { languageGroup: string, probability: number }[] = []
|
|
93
|
+
|
|
94
|
+
for (let i = 0; i < languageGroupProbabilities.length; i++) {
|
|
95
|
+
languageGroupResults.push({
|
|
96
|
+
languageGroup: this.languageGroupDictionary[i],
|
|
97
|
+
probability: languageGroupProbabilities[i]
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
logger.end()
|
|
102
|
+
|
|
103
|
+
return { languageResults, languageGroupResults }
|
|
104
|
+
}
|
|
105
|
+
}
|