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.
Files changed (122) hide show
  1. package/data/schemas/options.json +16 -0
  2. package/dist/api/Alignment.js +2 -2
  3. package/dist/api/Alignment.js.map +1 -1
  4. package/dist/api/Recognition.js +2 -2
  5. package/dist/api/Recognition.js.map +1 -1
  6. package/dist/api/Synthesis.js +5 -4
  7. package/dist/api/Synthesis.js.map +1 -1
  8. package/dist/api/Translation.js +2 -2
  9. package/dist/api/Translation.js.map +1 -1
  10. package/dist/audio/AudioUtilities.d.ts +1 -0
  11. package/dist/audio/AudioUtilities.js +25 -7
  12. package/dist/audio/AudioUtilities.js.map +1 -1
  13. package/dist/cli/CLI.js +2 -2
  14. package/dist/cli/CLI.js.map +1 -1
  15. package/dist/recognition/WhisperSTT.js +2 -2
  16. package/dist/recognition/WhisperSTT.js.map +1 -1
  17. package/dist/subtitles/Subtitles.d.ts +10 -7
  18. package/dist/subtitles/Subtitles.js +268 -207
  19. package/dist/subtitles/Subtitles.js.map +1 -1
  20. package/docs/Options.md +4 -2
  21. package/package.json +7 -6
  22. package/src/alignment/DTWMfccSequenceAlignment.ts +43 -0
  23. package/src/alignment/DTWSequenceAlignment.ts +121 -0
  24. package/src/alignment/DTWSequenceAlignmentWindowed.ts +210 -0
  25. package/src/alignment/LevenshteinSequenceAlignment.ts +126 -0
  26. package/src/alignment/SpeechAlignment.ts +488 -0
  27. package/src/api/API.ts +12 -0
  28. package/src/api/APIOptions.ts +15 -0
  29. package/src/api/Alignment.ts +329 -0
  30. package/src/api/Common.ts +16 -0
  31. package/src/api/Denoising.ts +120 -0
  32. package/src/api/LanguageDetection.ts +286 -0
  33. package/src/api/Recognition.ts +344 -0
  34. package/src/api/Synthesis.ts +1735 -0
  35. package/src/api/Translation.ts +143 -0
  36. package/src/api/Vad.ts +172 -0
  37. package/src/audio/AudioBufferConversion.ts +248 -0
  38. package/src/audio/AudioPlayer.ts +358 -0
  39. package/src/audio/AudioRecorder.ts +91 -0
  40. package/src/audio/AudioUtilities.ts +392 -0
  41. package/src/audio/SoxPath.ts +24 -0
  42. package/src/cli/CLI.ts +1360 -0
  43. package/src/cli/CLIConfigFile.ts +91 -0
  44. package/src/cli/CLILauncher.ts +26 -0
  45. package/src/cli/CLIOptionsSchema.ts +54 -0
  46. package/src/cli/CLIParser.ts +41 -0
  47. package/src/cli/CLIStarter.ts +40 -0
  48. package/src/codecs/FFMpegTranscoder.ts +214 -0
  49. package/src/codecs/TIMITCodec.ts +17 -0
  50. package/src/codecs/WaveCodec.ts +260 -0
  51. package/src/denoising/RNNoise.ts +95 -0
  52. package/src/dsp/BiquadFilter.ts +488 -0
  53. package/src/dsp/FFT.ts +187 -0
  54. package/src/dsp/MFCC.ts +227 -0
  55. package/src/dsp/MelSpectogram.ts +145 -0
  56. package/src/dsp/Rubberband.ts +249 -0
  57. package/src/dsp/Sonic.ts +59 -0
  58. package/src/dsp/SpeexResampler.ts +79 -0
  59. package/src/math/VectorMath.ts +812 -0
  60. package/src/nlp/ChineseSegmentation.ts +68 -0
  61. package/src/nlp/CompromiseNLP.ts +113 -0
  62. package/src/nlp/EspeakPhonemizer.ts +168 -0
  63. package/src/nlp/IPA.ts +139 -0
  64. package/src/nlp/JapaneseSegmentation.ts +53 -0
  65. package/src/nlp/Lexicon.ts +119 -0
  66. package/src/nlp/PhoneConversion.ts +508 -0
  67. package/src/nlp/Segmentation.ts +237 -0
  68. package/src/nlp/TextNormalizer.ts +160 -0
  69. package/src/recognition/AmazonTranscribeSTT.ts +112 -0
  70. package/src/recognition/AzureCognitiveServicesSTT.ts +76 -0
  71. package/src/recognition/GoogleCloudSTT.ts +92 -0
  72. package/src/recognition/SileroSTT.ts +173 -0
  73. package/src/recognition/VoskSTT.ts +112 -0
  74. package/src/recognition/WhisperSTT.ts +1518 -0
  75. package/src/server/Client.ts +297 -0
  76. package/src/server/Server.ts +178 -0
  77. package/src/server/ServerStarter.ts +12 -0
  78. package/src/server/Worker.ts +400 -0
  79. package/src/server/WorkerStarter.ts +38 -0
  80. package/src/speech-language-detection/SileroLanguageDetection.ts +105 -0
  81. package/src/subtitles/Subtitles.ts +478 -0
  82. package/src/synthesis/AwsPollyTTS.ts +78 -0
  83. package/src/synthesis/AzureCognitiveServicesTTS.ts +146 -0
  84. package/src/synthesis/CoquiServerTTS.ts +29 -0
  85. package/src/synthesis/ElevenLabsTTS.ts +104 -0
  86. package/src/synthesis/EspeakTTS.ts +552 -0
  87. package/src/synthesis/FliteTTS.ts +387 -0
  88. package/src/synthesis/GoogleCloudTTS.ts +112 -0
  89. package/src/synthesis/GoogleTranslateTTS.ts +210 -0
  90. package/src/synthesis/MicrosoftEdgeTTS.ts +298 -0
  91. package/src/synthesis/SamTTS.ts +30 -0
  92. package/src/synthesis/SapiTTS.ts +222 -0
  93. package/src/synthesis/StreamlabsPollyTTS.ts +114 -0
  94. package/src/synthesis/SvoxPicoTTS.ts +318 -0
  95. package/src/synthesis/VitsTTS.ts +734 -0
  96. package/src/tests/Test.ts +24 -0
  97. package/src/text-language-detection/FastTextLanguageDetection.ts +53 -0
  98. package/src/text-language-detection/TinyLDLanguageDetection.ts +16 -0
  99. package/src/typings/Fillers.d.ts +41 -0
  100. package/src/utilities/BinaryArrayConversion.ts +159 -0
  101. package/src/utilities/Compression.ts +91 -0
  102. package/src/utilities/FileDownloader.ts +201 -0
  103. package/src/utilities/FileSystem.ts +265 -0
  104. package/src/utilities/Hashing.ts +230 -0
  105. package/src/utilities/Locale.ts +119 -0
  106. package/src/utilities/Logger.ts +72 -0
  107. package/src/utilities/NdArrayUtilities.ts +31 -0
  108. package/src/utilities/ObjectUtilities.ts +169 -0
  109. package/src/utilities/OpenPromise.ts +13 -0
  110. package/src/utilities/PackageManager.ts +97 -0
  111. package/src/utilities/Queue.ts +17 -0
  112. package/src/utilities/RandomGenerator.ts +237 -0
  113. package/src/utilities/SignalChannel.ts +22 -0
  114. package/src/utilities/TarballMaker.ts +68 -0
  115. package/src/utilities/Timeline.ts +231 -0
  116. package/src/utilities/Timer.ts +93 -0
  117. package/src/utilities/Utilities.ts +574 -0
  118. package/src/utilities/WasmMemoryManager.ts +516 -0
  119. package/src/utilities/WebReader.ts +55 -0
  120. package/src/utilities/WikipediaReader.ts +41 -0
  121. package/src/voice-activity-detection/SileroVAD.ts +86 -0
  122. 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
+ }