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,286 @@
1
+ import { deepClone, extendDeep } from "../utilities/ObjectUtilities.js"
2
+
3
+ import { AudioSourceParam, RawAudio, ensureRawAudio, getRawAudioDuration, normalizeAudioLevel, sliceRawAudioByTime, trimAudioEnd } from "../audio/AudioUtilities.js"
4
+ import { Logger } from "../utilities/Logger.js"
5
+
6
+ import * as API from "./API.js"
7
+ import { logToStderr } from "../utilities/Utilities.js"
8
+ import path from "path"
9
+ import { type WhisperOptions } from "../recognition/WhisperSTT.js"
10
+ import { formatLanguageCodeWithName, languageCodeToName } from "../utilities/Locale.js"
11
+ import { loadPackage } from "../utilities/PackageManager.js"
12
+ import chalk from "chalk"
13
+
14
+ const log = logToStderr
15
+
16
+ /////////////////////////////////////////////////////////////////////////////////////////////
17
+ // Speech language detection
18
+ /////////////////////////////////////////////////////////////////////////////////////////////
19
+ export async function detectSpeechLanguage(input: AudioSourceParam, options: SpeechLanguageDetectionOptions): Promise<SpeechLanguageDetectionResult> {
20
+ const logger = new Logger()
21
+
22
+ const startTime = logger.getTimestamp()
23
+
24
+ logger.start("Prepare for speech language detection")
25
+
26
+ const inputRawAudio = await ensureRawAudio(input)
27
+
28
+ let sourceRawAudio = await ensureRawAudio(inputRawAudio, 16000, 1)
29
+ sourceRawAudio = normalizeAudioLevel(sourceRawAudio)
30
+ sourceRawAudio.audioChannels[0] = trimAudioEnd(sourceRawAudio.audioChannels[0])
31
+
32
+ options = extendDeep(defaultSpeechLanguageDetectionOptions, options)
33
+
34
+ const defaultLanguage = options.defaultLanguage!
35
+ const fallbackThresholdProbability = options.fallbackThresholdProbability!
36
+
37
+ logger.start(`Initialize ${options.engine} module`)
38
+
39
+ let detectedLanguageProbabilities: LanguageDetectionResults
40
+
41
+ switch (options.engine) {
42
+ case "silero": {
43
+ const SileroLanguageDetection = await import("../speech-language-detection/SileroLanguageDetection.js")
44
+
45
+ logger.end()
46
+
47
+ const sileroOptions = options.silero!
48
+
49
+ const modelDir = await loadPackage("silero-lang-classifier-95")
50
+
51
+ const modelPath = path.join(modelDir, "lang_classifier_95.onnx")
52
+ const languageDictionaryPath = path.join(modelDir, "lang_dict_95.json")
53
+ const languageGroupDictionaryPath = path.join(modelDir, "lang_group_dict_95.json")
54
+
55
+ const languageResults = await SileroLanguageDetection.detectLanguage(
56
+ sourceRawAudio,
57
+ modelPath,
58
+ languageDictionaryPath,
59
+ languageGroupDictionaryPath)
60
+
61
+ detectedLanguageProbabilities = languageResults
62
+
63
+ break
64
+ }
65
+
66
+ case "whisper": {
67
+ const WhisperSTT = await import("../recognition/WhisperSTT.js")
68
+
69
+ const whisperOptions = options.whisper!
70
+
71
+ const { modelName, modelDir, tokenizerDir } = await WhisperSTT.loadPackagesAndGetPaths(whisperOptions.model, undefined)
72
+
73
+ logger.end()
74
+
75
+ detectedLanguageProbabilities = await WhisperSTT.detectLanguage(sourceRawAudio, modelName, modelDir, tokenizerDir)
76
+
77
+ break
78
+ }
79
+
80
+ default: {
81
+ throw new Error(`Engine '${options.engine}' is not supported`)
82
+ }
83
+ }
84
+
85
+ let detectedLanguage: string
86
+
87
+ if (detectedLanguageProbabilities.length == 0 ||
88
+ detectedLanguageProbabilities[0].probability < fallbackThresholdProbability) {
89
+
90
+ detectedLanguage = defaultLanguage
91
+ } else {
92
+ detectedLanguage = detectedLanguageProbabilities[0].language
93
+ }
94
+
95
+ logger.end()
96
+ logger.logDuration("\nTotal detection time", startTime, chalk.magentaBright)
97
+
98
+ return { detectedLanguage, detectedLanguageName: languageCodeToName(detectedLanguage), detectedLanguageProbabilities, inputRawAudio }
99
+ }
100
+
101
+ export interface SpeechLanguageDetectionResult {
102
+ detectedLanguage: string
103
+ detectedLanguageName: string
104
+ detectedLanguageProbabilities: LanguageDetectionResults
105
+ inputRawAudio: RawAudio
106
+ }
107
+
108
+ export async function detectSpeechLanguageByParts(sourceRawAudio: RawAudio, getResultsForAudioPart: (audioPart: RawAudio) => Promise<LanguageDetectionResults>, audioPartDuration = 30, hopDuration = 15) {
109
+ const logger = new Logger()
110
+
111
+ const audioDuration = getRawAudioDuration(sourceRawAudio)
112
+
113
+ const resultsForParts: LanguageDetectionResults[] = []
114
+
115
+ for (let audioTimeOffset = 0; audioTimeOffset < audioDuration; audioTimeOffset += hopDuration) {
116
+ const startOffset = audioTimeOffset
117
+ const endOffset = Math.min(audioTimeOffset + audioPartDuration, audioDuration)
118
+ const audioPartLength = endOffset - startOffset
119
+
120
+ logger.logTitledMessage(`\nDetecting speech language starting at audio offset`, `${startOffset.toFixed(1)}`, chalk.magentaBright)
121
+ const audioPart = sliceRawAudioByTime(sourceRawAudio, startOffset, endOffset)
122
+
123
+ const resultsForPart = await getResultsForAudioPart(audioPart)
124
+
125
+ resultsForParts.push(resultsForPart)
126
+
127
+ const sortedResultsForPart = deepClone(resultsForPart).sort((a, b) => b.probability - a.probability)
128
+
129
+ logger.logTitledMessage(`Top candidates`, `${formatLanguageCodeWithName(sortedResultsForPart[0].language)}: ${sortedResultsForPart[0].probability.toFixed(3)}, ${formatLanguageCodeWithName(sortedResultsForPart[1].language)}: ${sortedResultsForPart[1].probability.toFixed(3)}, ${formatLanguageCodeWithName(sortedResultsForPart[3].language)}: ${sortedResultsForPart[3].probability.toFixed(3)}`)
130
+
131
+ if (audioPartLength < audioPartDuration) {
132
+ break
133
+ }
134
+ }
135
+
136
+ const averagedResults: LanguageDetectionResults = deepClone(resultsForParts[0])
137
+ averagedResults.forEach(entry => { entry.probability = 0.0 })
138
+
139
+ for (const partResults of resultsForParts) {
140
+ for (let i = 0; i < partResults.length; i++) {
141
+ averagedResults[i].probability += partResults[i].probability
142
+ }
143
+ }
144
+
145
+ for (const result of averagedResults) {
146
+ result.probability /= resultsForParts.length
147
+ }
148
+
149
+ return averagedResults
150
+ }
151
+
152
+ export type SpeechLanguageDetectionEngine = "silero" | "whisper"
153
+
154
+ export interface SpeechLanguageDetectionOptions {
155
+ engine?: SpeechLanguageDetectionEngine
156
+ defaultLanguage?: string,
157
+ fallbackThresholdProbability?: number
158
+
159
+ silero?: {
160
+ }
161
+
162
+ whisper?: WhisperOptions
163
+ }
164
+
165
+ export const defaultSpeechLanguageDetectionOptions: SpeechLanguageDetectionOptions = {
166
+ engine: "silero",
167
+
168
+ silero: {
169
+ },
170
+
171
+ whisper: {
172
+ model: "tiny",
173
+ }
174
+ }
175
+
176
+ /////////////////////////////////////////////////////////////////////////////////////////////
177
+ // Text language detection
178
+ /////////////////////////////////////////////////////////////////////////////////////////////
179
+ export async function detectTextLanguage(input: string, options: TextLanguageDetectionOptions): Promise<TextLanguageDetectionResult> {
180
+ const logger = new Logger()
181
+
182
+ options = extendDeep(defaultTextLanguageDetectionOptions, options)
183
+
184
+ const defaultLanguage = options.defaultLanguage!
185
+ const fallbackThresholdProbability = options.fallbackThresholdProbability!
186
+
187
+ let detectedLanguageProbabilities: LanguageDetectionResults
188
+
189
+ logger.start(`Initialize ${options.engine} module`)
190
+
191
+ switch (options.engine) {
192
+ case "tinyld": {
193
+ const { detectLanguage } = await import("../text-language-detection/TinyLDLanguageDetection.js")
194
+
195
+ logger.start("Detecting text language using tinyld")
196
+
197
+ detectedLanguageProbabilities = await detectLanguage(input)
198
+
199
+ break
200
+ }
201
+
202
+ case "fasttext": {
203
+ const { detectLanguage } = await import("../text-language-detection/FastTextLanguageDetection.js")
204
+
205
+ logger.start("Detecting text language using FastText")
206
+
207
+ detectedLanguageProbabilities = await detectLanguage(input)
208
+
209
+ break
210
+ }
211
+
212
+ default: {
213
+ throw new Error(`Engine '${options.engine}' is not supported`)
214
+ }
215
+ }
216
+
217
+ let detectedLanguage: string
218
+
219
+ if (detectedLanguageProbabilities.length == 0 ||
220
+ detectedLanguageProbabilities[0].probability < fallbackThresholdProbability) {
221
+
222
+ detectedLanguage = defaultLanguage
223
+ } else {
224
+ detectedLanguage = detectedLanguageProbabilities[0].language
225
+ }
226
+
227
+ logger.end()
228
+
229
+ return { detectedLanguage, detectedLanguageName: languageCodeToName(detectedLanguage), detectedLanguageProbabilities }
230
+ }
231
+
232
+ export interface TextLanguageDetectionResult {
233
+ detectedLanguage: string
234
+ detectedLanguageName: string
235
+ detectedLanguageProbabilities: LanguageDetectionResults
236
+ }
237
+
238
+ export type LanguageDetectionResults = LanguageDetectionResultsEntry[]
239
+ export type LanguageDetectionResultsEntry = { language: string, languageName: string, probability: number }
240
+
241
+ export type LanguageDetectionGroupResults = LanguageDetectionGroupResultsEntry[]
242
+ export type LanguageDetectionGroupResultsEntry = { languageGroup: string, probability: number }
243
+
244
+ export type TextLanguageDetectionEngine = "tinyld" | "fasttext"
245
+
246
+ export type TextLanguageDetectionOptions = {
247
+ engine?: TextLanguageDetectionEngine,
248
+ defaultLanguage?: string,
249
+ fallbackThresholdProbability?: number
250
+ }
251
+
252
+ export const defaultTextLanguageDetectionOptions: TextLanguageDetectionOptions = {
253
+ engine: "tinyld",
254
+ defaultLanguage: "en",
255
+ fallbackThresholdProbability: 0.05
256
+ }
257
+
258
+ export const speechLanguageDetectionEngines: API.EngineMetadata[] = [
259
+ {
260
+ id: 'silero',
261
+ name: 'Silero',
262
+ description: 'A speech language classification model by Silero.',
263
+ type: 'local'
264
+ },
265
+ {
266
+ id: 'whisper',
267
+ name: 'OpenAI Whisper',
268
+ description: 'Uses the language token produced by the Whisper model to guess the language of the speech (first 30 seconds only).',
269
+ type: 'local'
270
+ },
271
+ ]
272
+
273
+ export const textLanguageDetectionEngines: API.EngineMetadata[] = [
274
+ {
275
+ id: 'tinyld',
276
+ name: 'TinyLD',
277
+ description: 'A simple language detection library.',
278
+ type: 'local'
279
+ },
280
+ {
281
+ id: 'fasttext',
282
+ name: 'FastText',
283
+ description: 'a library for word representations and sentence classification by Facebook research.',
284
+ type: 'local'
285
+ },
286
+ ]
@@ -0,0 +1,344 @@
1
+ import { extendDeep } from "../utilities/ObjectUtilities.js"
2
+
3
+ import { logToStderr } from "../utilities/Utilities.js"
4
+ import { AudioSourceParam, RawAudio, ensureRawAudio, normalizeAudioLevel, trimAudioEnd } from "../audio/AudioUtilities.js"
5
+ import { Logger } from "../utilities/Logger.js"
6
+
7
+ import * as API from "./API.js"
8
+ import { Timeline, addWordTextOffsetsToTimeline, wordTimelineToSegmentSentenceTimeline } from "../utilities/Timeline.js"
9
+ import { whisperOptionsDefaults, type WhisperOptions } from "../recognition/WhisperSTT.js"
10
+ import { formatLanguageCodeWithName, getShortLanguageCode, normalizeLanguageCode } from "../utilities/Locale.js"
11
+ import { loadPackage } from "../utilities/PackageManager.js"
12
+ import chalk from "chalk"
13
+ import { SubtitlesConfig, defaultSubtitlesBaseConfig } from "../subtitles/Subtitles.js"
14
+
15
+ const log = logToStderr
16
+
17
+ export async function recognize(input: AudioSourceParam, options: RecognitionOptions): Promise<RecognitionResult> {
18
+ const logger = new Logger()
19
+ const startTimestamp = logger.getTimestamp()
20
+
21
+ logger.start("Prepare for recognition")
22
+
23
+ const inputRawAudio = await ensureRawAudio(input)
24
+
25
+ let sourceRawAudio = await ensureRawAudio(inputRawAudio, 16000, 1)
26
+ sourceRawAudio = normalizeAudioLevel(sourceRawAudio)
27
+ sourceRawAudio.audioChannels[0] = trimAudioEnd(sourceRawAudio.audioChannels[0])
28
+
29
+ options = extendDeep(defaultRecognitionOptions, options)
30
+
31
+ const engine = options.engine!
32
+
33
+ if (!options.language) { // && options.engine != "whisper") {
34
+ logger.start("No language specified. Detecting speech language")
35
+ const { detectedLanguage } = await API.detectSpeechLanguage(inputRawAudio, options.languageDetection || {})
36
+
37
+ logger.end()
38
+ logger.logTitledMessage('Language detected', formatLanguageCodeWithName(detectedLanguage))
39
+
40
+ options.language = detectedLanguage
41
+ }
42
+
43
+ let language = normalizeLanguageCode(options.language)
44
+
45
+ let transcript: string
46
+ let timeline: Timeline | undefined
47
+
48
+ logger.start(`Load ${engine} module`)
49
+
50
+ switch (engine) {
51
+ case "whisper": {
52
+ const WhisperSTT = await import("../recognition/WhisperSTT.js")
53
+
54
+ const whisperOptions = options.whisper!
55
+
56
+ const shortLanguageCode = getShortLanguageCode(language)
57
+
58
+ const { modelName, modelDir, tokenizerDir } = await WhisperSTT.loadPackagesAndGetPaths(whisperOptions.model, shortLanguageCode)
59
+
60
+ if (shortLanguageCode != "en" && modelName.endsWith(".en")) {
61
+ throw new Error(`The model '${modelName}' is English only and cannot transcribe language '${shortLanguageCode}'`)
62
+ }
63
+
64
+ logger.end();
65
+
66
+ ({ transcript, timeline } = await WhisperSTT.recognize(sourceRawAudio, modelName, modelDir, tokenizerDir, "transcribe", language, whisperOptions))
67
+
68
+ break
69
+ }
70
+
71
+ case "vosk": {
72
+ const VoskSTT = await import("../recognition/VoskSTT.js")
73
+
74
+ try {
75
+ await import('@echogarden/vosk')
76
+ } catch (e) {
77
+ log(e)
78
+ throw new Error(`The vosk npm package, which is required for Vosk support, was not found, or had an error loading. If missing, you can install it by running 'npm install @echogarden/vosk -g'.`)
79
+ }
80
+
81
+ const voskOptions = options.vosk!
82
+
83
+ const modelPath = voskOptions.modelPath
84
+
85
+ if (!modelPath) {
86
+ throw new Error("Vosk models are not currently auto-downloaded. You'll need to download a model manually and set a model path in 'vosk.modelPath'.")
87
+ }
88
+
89
+ logger.end();
90
+
91
+ ({ transcript, timeline } = await VoskSTT.recognize(sourceRawAudio, modelPath, true))
92
+
93
+ break
94
+ }
95
+
96
+ case "silero": {
97
+ const SileroSTT = await import("../recognition/SileroSTT.js")
98
+
99
+ const sileroOptions = options.silero!
100
+
101
+ let modelPath = sileroOptions.modelPath
102
+
103
+ if (!modelPath) {
104
+ const shortLanguageCode = getShortLanguageCode(language)
105
+ const packageName = SileroSTT.languageCodeToPackageName[shortLanguageCode]
106
+
107
+ if (!packageName) {
108
+ throw new Error(`Language '${shortLanguageCode}' is not supported by Silero`)
109
+ }
110
+
111
+ modelPath = await loadPackage(packageName)
112
+ }
113
+
114
+ logger.end();
115
+
116
+ ({ transcript, timeline } = await SileroSTT.recognize(sourceRawAudio, modelPath))
117
+
118
+ break
119
+ }
120
+
121
+ case "google-cloud": {
122
+ const GoogleCloudSTT = await import("../recognition/GoogleCloudSTT.js")
123
+
124
+ const apiKey = options.googleCloud!.apiKey
125
+
126
+ if (!apiKey) {
127
+ throw new Error(`No API key given`)
128
+ }
129
+
130
+ logger.end();
131
+
132
+ ({ transcript, timeline } = await GoogleCloudSTT.recognize(sourceRawAudio, apiKey, language))
133
+
134
+ break
135
+ }
136
+
137
+ case "microsoft-azure": {
138
+ const AzureCognitiveServicesSTT = await import("../recognition/AzureCognitiveServicesSTT.js")
139
+
140
+ const subscriptionKey = options.microsoftAzure!.subscriptionKey
141
+
142
+ if (!subscriptionKey) {
143
+ throw new Error(`No subscription key given`)
144
+ }
145
+
146
+ const serviceRegion = options.microsoftAzure!.serviceRegion
147
+
148
+ if (!serviceRegion) {
149
+ throw new Error(`No service region given`)
150
+ }
151
+
152
+ logger.end();
153
+
154
+ ({ transcript, timeline } = await AzureCognitiveServicesSTT.recognize(sourceRawAudio, subscriptionKey, serviceRegion, language))
155
+
156
+ break
157
+ }
158
+
159
+ case "amazon-transcribe": {
160
+ const AmazonTranscribeSTT = await import("../recognition/AmazonTranscribeSTT.js")
161
+
162
+ const region = options.amazonTranscribe!.region
163
+
164
+ if (!region) {
165
+ throw new Error(`No region given`)
166
+ }
167
+
168
+ const accessKeyId = options.amazonTranscribe!.accessKeyId
169
+
170
+ if (!accessKeyId) {
171
+ throw new Error(`No access key id given`)
172
+ }
173
+
174
+ const secretAccessKey = options.amazonTranscribe!.secretAccessKey
175
+
176
+ if (!secretAccessKey) {
177
+ throw new Error(`No secret access key given`)
178
+ }
179
+
180
+ logger.end();
181
+
182
+ ({ transcript, timeline } = await AmazonTranscribeSTT.recgonize(sourceRawAudio, language, region, accessKeyId, secretAccessKey))
183
+
184
+ break
185
+ }
186
+
187
+ default: {
188
+ throw new Error(`Engine '${options.engine}' is not supported`)
189
+ }
190
+ }
191
+
192
+ if (!timeline) {
193
+ logger.start(`Align audio to transcript`)
194
+ const alignmentOptions: API.AlignmentOptions = extendDeep(options.alignment, { language: language })
195
+
196
+ const { wordTimeline } = await API.align(sourceRawAudio, transcript, alignmentOptions)
197
+
198
+ timeline = wordTimeline
199
+ }
200
+
201
+ addWordTextOffsetsToTimeline(timeline, transcript)
202
+
203
+ const { segmentTimeline } = await wordTimelineToSegmentSentenceTimeline(timeline, transcript, language, 'single', 'preserve')
204
+
205
+ logger.end()
206
+ logger.logDuration('Total recognition time', startTimestamp, chalk.magentaBright)
207
+
208
+ return { transcript, timeline: segmentTimeline, wordTimeline: timeline, inputRawAudio, language }
209
+ }
210
+
211
+ export interface RecognitionResult {
212
+ transcript: string
213
+ timeline: Timeline
214
+ wordTimeline: Timeline
215
+ language: string
216
+ inputRawAudio: RawAudio
217
+ }
218
+
219
+ export type RecognitionEngine = "whisper" | "vosk" | "silero" | "google-cloud" | "microsoft-azure" | "amazon-transcribe"
220
+
221
+ export interface RecognitionOptions {
222
+ engine?: RecognitionEngine
223
+
224
+ language?: string
225
+
226
+ maxAlternatives?: number
227
+
228
+ alignment?: API.AlignmentOptions
229
+
230
+ languageDetection?: API.SpeechLanguageDetectionOptions
231
+
232
+ subtitles?: SubtitlesConfig
233
+
234
+ whisper?: WhisperOptions
235
+
236
+ vosk?: {
237
+ modelPath?: string
238
+ }
239
+
240
+ silero?: {
241
+ modelPath?: string
242
+ }
243
+
244
+ googleCloud?: {
245
+ apiKey?: string
246
+ alternativeLanguageCodes?: string[]
247
+ profanityFilter?: boolean
248
+ autoPunctuation?: boolean
249
+ useEnhancedModel?: boolean
250
+ }
251
+
252
+ microsoftAzure?: {
253
+ subscriptionKey?: string
254
+ serviceRegion?: string
255
+ }
256
+
257
+ amazonTranscribe?: {
258
+ region?: string
259
+ accessKeyId?: string
260
+ secretAccessKey?: string
261
+ }
262
+ }
263
+
264
+ export const defaultRecognitionOptions: RecognitionOptions = {
265
+ engine: "whisper",
266
+
267
+ language: undefined,
268
+
269
+ maxAlternatives: 1,
270
+
271
+ alignment: undefined,
272
+
273
+ languageDetection: undefined,
274
+
275
+ subtitles: defaultSubtitlesBaseConfig,
276
+
277
+ whisper: whisperOptionsDefaults,
278
+
279
+ vosk: {
280
+ modelPath: undefined
281
+ },
282
+
283
+ silero: {
284
+ modelPath: undefined
285
+ },
286
+
287
+ googleCloud: {
288
+ apiKey: undefined,
289
+ alternativeLanguageCodes: [],
290
+ profanityFilter: false,
291
+ autoPunctuation: true,
292
+ useEnhancedModel: true,
293
+ },
294
+
295
+ microsoftAzure: {
296
+ subscriptionKey: undefined,
297
+ serviceRegion: undefined
298
+ },
299
+
300
+ amazonTranscribe: {
301
+ region: undefined,
302
+ accessKeyId: undefined,
303
+ secretAccessKey: undefined,
304
+ }
305
+ }
306
+
307
+ export const recognitionEngines: API.EngineMetadata[] = [
308
+ {
309
+ id: 'whisper',
310
+ name: 'OpenAI Whisper',
311
+ description: 'A high accuracy transformer-based architecture by OpenAI.',
312
+ type: 'local'
313
+ },
314
+ {
315
+ id: 'vosk',
316
+ name: 'Vosk',
317
+ description: 'A speech recognition toolkit.',
318
+ type: 'local'
319
+ },
320
+ {
321
+ id: 'silero',
322
+ name: 'Silero',
323
+ description: 'Speech recognition models.',
324
+ type: 'local'
325
+ },
326
+ {
327
+ id: 'google-cloud',
328
+ name: 'Google Cloud',
329
+ description: 'Google Cloud speech-to-text service.',
330
+ type: 'cloud'
331
+ },
332
+ {
333
+ id: 'microsoft-azure',
334
+ name: 'Azure Cognitive Services',
335
+ description: 'Microsoft Azure speech-to-text service.',
336
+ type: 'cloud'
337
+ },
338
+ {
339
+ id: 'amazon-transcribe',
340
+ name: 'Amazon Transcribe',
341
+ description: 'Amazon cloud speech-to-text service.',
342
+ type: 'cloud'
343
+ },
344
+ ]