echogarden 1.4.4 → 1.6.0

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 (240) hide show
  1. package/data/schemas/options.json +310 -25
  2. package/dist/alignment/DTWMfccSequenceAlignment.d.ts +1 -1
  3. package/dist/alignment/DTWMfccSequenceAlignment.js +5 -5
  4. package/dist/alignment/DTWSequenceAlignmentWindowed.js +1 -3
  5. package/dist/alignment/DTWSequenceAlignmentWindowed.js.map +1 -1
  6. package/dist/alignment/{TextAlignment.d.ts → SemanticTextAlignment.d.ts} +4 -2
  7. package/dist/alignment/SemanticTextAlignment.js +336 -0
  8. package/dist/alignment/SemanticTextAlignment.js.map +1 -0
  9. package/dist/alignment/SpeechAlignment.d.ts +4 -3
  10. package/dist/alignment/SpeechAlignment.js +130 -39
  11. package/dist/alignment/SpeechAlignment.js.map +1 -1
  12. package/dist/api/API.d.ts +7 -3
  13. package/dist/api/API.js +7 -2
  14. package/dist/api/API.js.map +1 -1
  15. package/dist/api/APIOptions.d.ts +4 -1
  16. package/dist/api/Alignment.d.ts +1 -1
  17. package/dist/api/Alignment.js +13 -5
  18. package/dist/api/Alignment.js.map +1 -1
  19. package/dist/api/LanguageDetectionCommon.d.ts +6 -0
  20. package/dist/api/LanguageDetectionCommon.js +2 -0
  21. package/dist/api/LanguageDetectionCommon.js.map +1 -0
  22. package/dist/api/Recognition.js.map +1 -1
  23. package/dist/api/{LanguageDetection.d.ts → SpeechLanguageDetection.d.ts} +1 -25
  24. package/dist/api/{LanguageDetection.js → SpeechLanguageDetection.js} +1 -68
  25. package/dist/api/SpeechLanguageDetection.js.map +1 -0
  26. package/dist/api/{Translation.js → SpeechTranslation.js} +3 -3
  27. package/dist/api/SpeechTranslation.js.map +1 -0
  28. package/dist/api/Synthesis.d.ts +0 -1
  29. package/dist/api/Synthesis.js +4 -4
  30. package/dist/api/TextLanguageDetection.d.ts +21 -0
  31. package/dist/api/TextLanguageDetection.js +67 -0
  32. package/dist/api/TextLanguageDetection.js.map +1 -0
  33. package/dist/api/TextTranslation.d.ts +25 -0
  34. package/dist/api/TextTranslation.js +101 -0
  35. package/dist/api/TextTranslation.js.map +1 -0
  36. package/dist/api/TimelineTranslationAlignment.d.ts +23 -0
  37. package/dist/api/TimelineTranslationAlignment.js +92 -0
  38. package/dist/api/TimelineTranslationAlignment.js.map +1 -0
  39. package/dist/api/TranscriptAndTranslationAlignment.d.ts +35 -0
  40. package/dist/api/TranscriptAndTranslationAlignment.js +78 -0
  41. package/dist/api/TranscriptAndTranslationAlignment.js.map +1 -0
  42. package/dist/api/TranslationAlignment.d.ts +4 -3
  43. package/dist/api/TranslationAlignment.js +9 -8
  44. package/dist/api/TranslationAlignment.js.map +1 -1
  45. package/dist/api/VoiceActivityDetection.js +16 -1
  46. package/dist/api/VoiceActivityDetection.js.map +1 -1
  47. package/dist/audio/AudioBufferConversion.d.ts +0 -1
  48. package/dist/audio/AudioPlayer.d.ts +0 -1
  49. package/dist/audio/AudioPlayer.js +62 -41
  50. package/dist/audio/AudioPlayer.js.map +1 -1
  51. package/dist/audio/AudioUtilities.d.ts +0 -1
  52. package/dist/cli/CLI.d.ts +28 -7
  53. package/dist/cli/CLI.js +265 -37
  54. package/dist/cli/CLI.js.map +1 -1
  55. package/dist/codecs/FFMpegTranscoder.d.ts +0 -1
  56. package/dist/codecs/FFMpegTranscoder.js +7 -0
  57. package/dist/codecs/FFMpegTranscoder.js.map +1 -1
  58. package/dist/codecs/TIMITCodec.d.ts +0 -1
  59. package/dist/codecs/WaveCodec.d.ts +0 -1
  60. package/dist/dsp/FFT.d.ts +1 -1
  61. package/dist/dsp/FFT.js +6 -0
  62. package/dist/dsp/FFT.js.map +1 -1
  63. package/dist/dsp/KWeightingFilter.js +1 -1
  64. package/dist/dsp/KWeightingFilter.js.map +1 -1
  65. package/dist/dsp/MelSpectogram.d.ts +3 -2
  66. package/dist/dsp/MelSpectogram.js +14 -8
  67. package/dist/dsp/MelSpectogram.js.map +1 -1
  68. package/dist/math/VectorMath.d.ts +9 -9
  69. package/dist/math/VectorMath.js +10 -10
  70. package/dist/math/VectorMath.js.map +1 -1
  71. package/dist/nlp/ChineseSegmentation.js +4 -4
  72. package/dist/nlp/ChineseSegmentation.js.map +1 -1
  73. package/dist/nlp/Segmentation.d.ts +2 -2
  74. package/dist/nlp/Segmentation.js +20 -13
  75. package/dist/nlp/Segmentation.js.map +1 -1
  76. package/dist/recognition/OpenAICloudSTT.d.ts +2 -1
  77. package/dist/recognition/OpenAICloudSTT.js +30 -19
  78. package/dist/recognition/OpenAICloudSTT.js.map +1 -1
  79. package/dist/recognition/SileroSTT.d.ts +0 -1
  80. package/dist/recognition/WhisperCppSTT.d.ts +3 -3
  81. package/dist/recognition/WhisperCppSTT.js +21 -9
  82. package/dist/recognition/WhisperCppSTT.js.map +1 -1
  83. package/dist/recognition/WhisperSTT.d.ts +9 -6
  84. package/dist/recognition/WhisperSTT.js +227 -46
  85. package/dist/recognition/WhisperSTT.js.map +1 -1
  86. package/dist/server/Client.d.ts +3 -4
  87. package/dist/server/Client.js.map +1 -1
  88. package/dist/server/Worker.d.ts +3 -3
  89. package/dist/server/Worker.js +3 -2
  90. package/dist/server/Worker.js.map +1 -1
  91. package/dist/source-separation/MDXNetSourceSeparation.d.ts +0 -1
  92. package/dist/source-separation/MDXNetSourceSeparation.js +1 -1
  93. package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
  94. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.d.ts +12 -0
  95. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js +68 -0
  96. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js.map +1 -0
  97. package/dist/speech-language-detection/SileroLanguageDetection.d.ts +1 -2
  98. package/dist/speech-language-detection/SileroLanguageDetection.js +1 -1
  99. package/dist/speech-language-detection/SileroLanguageDetection.js.map +1 -1
  100. package/dist/subtitles/Subtitles.js +2 -2
  101. package/dist/subtitles/Subtitles.js.map +1 -1
  102. package/dist/synthesis/GoogleCloudTTS.d.ts +0 -1
  103. package/dist/synthesis/GoogleTranslateTTS.d.ts +0 -1
  104. package/dist/synthesis/GoogleTranslateTTS.js +6 -21
  105. package/dist/synthesis/GoogleTranslateTTS.js.map +1 -1
  106. package/dist/synthesis/StreamlabsPollyTTS.d.ts +0 -1
  107. package/dist/synthesis/VitsTTS.d.ts +0 -1
  108. package/dist/synthesis/VitsTTS.js +30 -0
  109. package/dist/synthesis/VitsTTS.js.map +1 -1
  110. package/dist/tests/Test.js +0 -31
  111. package/dist/tests/Test.js.map +1 -1
  112. package/dist/text-language-detection/FastTextLanguageDetection.d.ts +1 -1
  113. package/dist/text-language-detection/FastTextLanguageDetection.js.map +1 -1
  114. package/dist/text-language-detection/TinyLDLanguageDetection.d.ts +1 -1
  115. package/dist/text-language-detection/TinyLDLanguageDetection.js.map +1 -1
  116. package/dist/text-translation/DeepLTextTranslation.d.ts +2 -0
  117. package/dist/text-translation/DeepLTextTranslation.js +67 -0
  118. package/dist/text-translation/DeepLTextTranslation.js.map +1 -0
  119. package/dist/text-translation/GoogleTranslateTextTranslation.d.ts +10 -0
  120. package/dist/text-translation/GoogleTranslateTextTranslation.js +554 -0
  121. package/dist/text-translation/GoogleTranslateTextTranslation.js.map +1 -0
  122. package/dist/text-translation/NLLBTextTranslation.d.ts +2 -1
  123. package/dist/text-translation/NLLBTextTranslation.js +249 -19
  124. package/dist/text-translation/NLLBTextTranslation.js.map +1 -1
  125. package/dist/utilities/BinaryArrayConversion.d.ts +0 -1
  126. package/dist/utilities/BrowserRequestHeaders.d.ts +6 -0
  127. package/dist/utilities/BrowserRequestHeaders.js +52 -0
  128. package/dist/utilities/BrowserRequestHeaders.js.map +1 -0
  129. package/dist/utilities/BufferFileReadStream.d.ts +20 -0
  130. package/dist/utilities/BufferFileReadStream.js +81 -0
  131. package/dist/utilities/BufferFileReadStream.js.map +1 -0
  132. package/dist/utilities/DynamicUint8Array.d.ts +9 -0
  133. package/dist/utilities/DynamicUint8Array.js +31 -0
  134. package/dist/utilities/DynamicUint8Array.js.map +1 -0
  135. package/dist/utilities/FileSystem.d.ts +0 -2
  136. package/dist/utilities/Hashing.d.ts +3 -10
  137. package/dist/utilities/Hashing.js +10 -127
  138. package/dist/utilities/Hashing.js.map +1 -1
  139. package/dist/utilities/LEB128.d.ts +15 -5
  140. package/dist/utilities/LEB128.js +199 -119
  141. package/dist/utilities/LEB128.js.map +1 -1
  142. package/dist/utilities/LPVarInt.d.ts +11 -0
  143. package/dist/utilities/LPVarInt.js +187 -0
  144. package/dist/utilities/LPVarInt.js.map +1 -0
  145. package/dist/utilities/Locale.d.ts +1 -1
  146. package/dist/utilities/Locale.js +1 -1
  147. package/dist/utilities/OnnxUtilities.d.ts +1 -2
  148. package/dist/utilities/PVarInt.d.ts +4 -0
  149. package/dist/utilities/PVarInt.js +166 -0
  150. package/dist/utilities/PVarInt.js.map +1 -0
  151. package/dist/utilities/PackageManager.js +48 -25
  152. package/dist/utilities/PackageManager.js.map +1 -1
  153. package/dist/utilities/RandomGenerator.d.ts +3 -17
  154. package/dist/utilities/RandomGenerator.js +12 -81
  155. package/dist/utilities/RandomGenerator.js.map +1 -1
  156. package/dist/utilities/Timeline.d.ts +2 -0
  157. package/dist/utilities/Timeline.js +129 -20
  158. package/dist/utilities/Timeline.js.map +1 -1
  159. package/dist/utilities/Utilities.d.ts +1 -3
  160. package/dist/utilities/Utilities.js +30 -3
  161. package/dist/utilities/Utilities.js.map +1 -1
  162. package/dist/utilities/VarInt.d.ts +4 -0
  163. package/dist/utilities/VarInt.js +166 -0
  164. package/dist/utilities/VarInt.js.map +1 -0
  165. package/dist/utilities/VirtualFileReadStream.d.ts +20 -0
  166. package/dist/utilities/VirtualFileReadStream.js +79 -0
  167. package/dist/utilities/VirtualFileReadStream.js.map +1 -0
  168. package/dist/utilities/WebReader.js +7 -23
  169. package/dist/utilities/WebReader.js.map +1 -1
  170. package/dist/voice-activity-detection/SileroVAD.d.ts +0 -1
  171. package/docs/API.md +105 -3
  172. package/docs/CLI.md +51 -1
  173. package/docs/Engines.md +32 -3
  174. package/docs/Options.md +53 -12
  175. package/docs/Tasklist.md +1 -13
  176. package/package.json +20 -24
  177. package/src/alignment/DTWMfccSequenceAlignment.ts +5 -5
  178. package/src/alignment/DTWSequenceAlignmentWindowed.ts +1 -3
  179. package/src/alignment/SemanticTextAlignment.ts +467 -0
  180. package/src/alignment/SpeechAlignment.ts +214 -56
  181. package/src/api/API.ts +18 -2
  182. package/src/api/APIOptions.ts +14 -1
  183. package/src/api/Alignment.ts +31 -9
  184. package/src/api/LanguageDetectionCommon.ts +7 -0
  185. package/src/api/Recognition.ts +2 -0
  186. package/src/api/{LanguageDetection.ts → SpeechLanguageDetection.ts} +1 -119
  187. package/src/api/{Translation.ts → SpeechTranslation.ts} +2 -2
  188. package/src/api/Synthesis.ts +4 -4
  189. package/src/api/TextLanguageDetection.ts +116 -0
  190. package/src/api/TextTranslation.ts +177 -0
  191. package/src/api/TimelineTranslationAlignment.ts +162 -0
  192. package/src/api/TranscriptAndTranslationAlignment.ts +164 -0
  193. package/src/api/TranslationAlignment.ts +12 -10
  194. package/src/api/VoiceActivityDetection.ts +24 -3
  195. package/src/audio/AudioPlayer.ts +2 -0
  196. package/src/cli/CLI.ts +376 -40
  197. package/src/codecs/FFMpegTranscoder.ts +6 -0
  198. package/src/dsp/FFT.ts +8 -2
  199. package/src/dsp/KWeightingFilter.ts +1 -1
  200. package/src/dsp/MelSpectogram.ts +17 -8
  201. package/src/math/VectorMath.ts +15 -15
  202. package/src/nlp/ChineseSegmentation.ts +6 -4
  203. package/src/nlp/Segmentation.ts +18 -13
  204. package/src/recognition/OpenAICloudSTT.ts +47 -29
  205. package/src/recognition/WhisperCppSTT.ts +26 -11
  206. package/src/recognition/WhisperSTT.ts +364 -49
  207. package/src/server/Client.ts +3 -2
  208. package/src/server/Worker.ts +3 -2
  209. package/src/source-separation/MDXNetSourceSeparation.ts +1 -1
  210. package/src/speech-embeddings/WavToVec2BertFeatureEmbeddings.ts +107 -0
  211. package/src/speech-language-detection/SileroLanguageDetection.ts +2 -1
  212. package/src/subtitles/Subtitles.ts +2 -2
  213. package/src/synthesis/GoogleTranslateTTS.ts +7 -21
  214. package/src/synthesis/VitsTTS.ts +31 -3
  215. package/src/tests/Test.ts +1 -38
  216. package/src/text-language-detection/FastTextLanguageDetection.ts +1 -1
  217. package/src/text-language-detection/TinyLDLanguageDetection.ts +1 -1
  218. package/src/text-translation/DeepLTextTranslation.ts +88 -0
  219. package/src/text-translation/GoogleTranslateTextTranslation.ts +667 -0
  220. package/src/text-translation/NLLBTextTranslation.ts +261 -21
  221. package/src/typings/Fillers.d.ts +25 -2
  222. package/src/utilities/BrowserRequestHeaders.ts +59 -0
  223. package/src/utilities/DynamicUint8Array.ts +39 -0
  224. package/src/utilities/Hashing.ts +14 -167
  225. package/src/utilities/LEB128.ts +273 -148
  226. package/src/utilities/LPVarInt.ts +292 -0
  227. package/src/utilities/Locale.ts +1 -1
  228. package/src/utilities/OnnxUtilities.ts +1 -1
  229. package/src/utilities/PackageManager.ts +51 -30
  230. package/src/utilities/RandomGenerator.ts +12 -113
  231. package/src/utilities/Timeline.ts +162 -23
  232. package/src/utilities/Utilities.ts +40 -3
  233. package/src/utilities/VirtualFileReadStream.ts +109 -0
  234. package/src/utilities/WebReader.ts +9 -23
  235. package/dist/alignment/TextAlignment.js +0 -156
  236. package/dist/alignment/TextAlignment.js.map +0 -1
  237. package/dist/api/LanguageDetection.js.map +0 -1
  238. package/dist/api/Translation.js.map +0 -1
  239. package/src/alignment/TextAlignment.ts +0 -234
  240. /package/dist/api/{Translation.d.ts → SpeechTranslation.d.ts} +0 -0
package/dist/cli/CLI.js CHANGED
@@ -5,7 +5,7 @@ import { getOptionTypeFromSchema } from './CLIOptionsSchema.js';
5
5
  import { parseConfigFile, parseJSONConfigFile } from './CLIConfigFile.js';
6
6
  import chalk from 'chalk';
7
7
  import { applyGainDecibels, encodeRawAudioToWave, getEmptyRawAudio, getRawAudioDuration, normalizeAudioLevel, sliceRawAudioByTime } from '../audio/AudioUtilities.js';
8
- import { subtitlesToText, subtitlesToTimeline, timelineToSubtitles } from '../subtitles/Subtitles.js';
8
+ import { subtitlesToText, timelineToSubtitles } from '../subtitles/Subtitles.js';
9
9
  import { Logger, resetActiveLogger } from '../utilities/Logger.js';
10
10
  import { isMainThread, parentPort } from 'node:worker_threads';
11
11
  import { encodeFromChannels, getDefaultFFMpegOptionsForSpeech } from '../codecs/FFMpegTranscoder.js';
@@ -170,19 +170,25 @@ const help = [
170
170
  `${executableName} ${chalk.magentaBright('speak-url')} url [output files...] [options...]`,
171
171
  ` Speak the HTML document on the given URL\n`,
172
172
  `${executableName} ${chalk.magentaBright('speak-wikipedia')} articleName [output files...] [options...]`,
173
- ` Speak the given wikipedia article, language edition can be specified by --language=<langCode>\n`,
173
+ ` Speak the given Wikipedia article. Language edition can be specified by --language=<langCode>\n`,
174
174
  `${executableName} ${chalk.magentaBright('transcribe')} audioFile [output files...] [options...]`,
175
- ` Transcribe audio file\n`,
176
- `${executableName} ${chalk.magentaBright('align')} audioFile referenceFile [output files...] [options...]`,
177
- ` Align audio file to the reference transcript file\n`,
178
- `${executableName} ${chalk.magentaBright('translate-speech')} inputFile [output files...] [options...]`,
179
- ` Transcribe audio file directly to a different language\n`,
180
- `${executableName} ${chalk.magentaBright('align-translation')} audioFile referenceFile [output files...] [options...]`,
181
- ` Align audio file to the reference translated transcript file\n`,
182
- `${executableName} ${chalk.magentaBright('detect-speech-language')} audioFile [output files...] [options...]`,
183
- ` Detect language of audio file\n`,
175
+ ` Transcribe a spoken audio file\n`,
176
+ `${executableName} ${chalk.magentaBright('align')} audioFile transcriptFile [output files...] [options...]`,
177
+ ` Align spoken audio file to its transcript\n`,
178
+ `${executableName} ${chalk.magentaBright('translate-text')} inputFile [output files...] [options...]`,
179
+ ` Translate text to a different language\n`,
180
+ `${executableName} ${chalk.magentaBright('translate-speech')} audioFile [output files...] [options...]`,
181
+ ` Transcribe spoken audio file directly to a different language\n`,
182
+ `${executableName} ${chalk.magentaBright('align-translation')} audioFile translatedTranscriptFile [output files...] [options...]`,
183
+ ` Align spoken audio file to its translated transcript\n`,
184
+ `${executableName} ${chalk.magentaBright('align-transcript-and-translation')} audioFile transcriptFile translatedTranscriptFile [output files...] [options...]`,
185
+ ` Align spoken audio file to both its transcript and its translated transcript using a two-stage approach.\n`,
186
+ `${executableName} ${chalk.magentaBright('align-timeline-translation')} timelineFile translatedFile [output files...] [options...]`,
187
+ ` Align a given timeline file to its translated text\n`,
184
188
  `${executableName} ${chalk.magentaBright('detect-text-language')} inputFile [output files...] [options...]`,
185
189
  ` Detect language of textual file\n`,
190
+ `${executableName} ${chalk.magentaBright('detect-speech-language')} audioFile [output files...] [options...]`,
191
+ ` Detect language of spoken audio file\n`,
186
192
  `${executableName} ${chalk.magentaBright('detect-voice-activity')} audioFile [output files...] [options...]`,
187
193
  ` Detect voice activity in audio file\n`,
188
194
  `${executableName} ${chalk.magentaBright('denoise')} audioFile [output files...] [options...]`,
@@ -221,6 +227,10 @@ async function startWithArgs(operationData) {
221
227
  await align(operationData);
222
228
  break;
223
229
  }
230
+ case 'translate-text': {
231
+ await translateText(operationData);
232
+ break;
233
+ }
224
234
  case 'translate-speech': {
225
235
  await translateSpeech(operationData);
226
236
  break;
@@ -229,6 +239,14 @@ async function startWithArgs(operationData) {
229
239
  await alignTranslation(operationData);
230
240
  break;
231
241
  }
242
+ case 'align-transcript-and-translation': {
243
+ await alignTranscriptAndTranslation(operationData);
244
+ break;
245
+ }
246
+ case 'align-timeline-translation': {
247
+ await alignTimelineTranslation(operationData);
248
+ break;
249
+ }
232
250
  case 'detect-language': {
233
251
  await detectLanguage(operationData, 'auto');
234
252
  break;
@@ -283,7 +301,7 @@ async function startWithArgs(operationData) {
283
301
  }
284
302
  }
285
303
  }
286
- async function speak(operationData) {
304
+ export async function speak(operationData) {
287
305
  const logger = new Logger();
288
306
  const { operationArgs, operation, operationOptionsLookup, cliOptions } = operationData;
289
307
  const mainArg = operationArgs[0];
@@ -345,8 +363,8 @@ async function speak(operationData) {
345
363
  }
346
364
  else if (sourceFileExtension == 'srt' || sourceFileExtension == 'vtt') {
347
365
  const fileContent = await readFile(sourceFile, { encoding: 'utf-8' });
348
- textSegments = subtitlesToTimeline(fileContent).map(entry => entry.text);
349
- //textSegments = [subtitlesToText(fileContent)]
366
+ //textSegments = subtitlesToTimeline(fileContent).map(entry => entry.text)
367
+ textSegments = [subtitlesToText(fileContent)];
350
368
  }
351
369
  else if (sourceFileExtension == 'xml' || sourceFileExtension == 'ssml') {
352
370
  options.ssml = true;
@@ -415,7 +433,7 @@ async function speak(operationData) {
415
433
  }
416
434
  logger.end();
417
435
  }
418
- async function transcribe(operationData) {
436
+ export async function transcribe(operationData) {
419
437
  const logger = new Logger();
420
438
  const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
421
439
  const sourceFilename = operationArgs[0];
@@ -458,7 +476,7 @@ async function transcribe(operationData) {
458
476
  await playAudioWithWordTimeline(normalizedAudioToPlay, wordTimeline, transcript);
459
477
  }
460
478
  }
461
- async function align(operationData) {
479
+ export async function align(operationData) {
462
480
  const logger = new Logger();
463
481
  const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
464
482
  const audioFilename = operationArgs[0];
@@ -531,13 +549,13 @@ async function align(operationData) {
531
549
  await playAudioWithWordTimeline(normalizedAudioToPlay, wordTimeline, transcript);
532
550
  }
533
551
  }
534
- async function alignTranslation(operationData) {
552
+ export async function alignTranslation(operationData) {
535
553
  const logger = new Logger();
536
554
  const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
537
555
  const audioFilename = operationArgs[0];
538
556
  const outputFilenames = operationArgs.slice(2);
539
557
  if (audioFilename == undefined) {
540
- throw new Error(`align-translation requires an argument containing the audio file path.`);
558
+ throw new Error(`align-translation requires a first argument containing the audio file path.`);
541
559
  }
542
560
  if (!existsSync(audioFilename)) {
543
561
  throw new Error(`The given source file '${audioFilename}' was not found.`);
@@ -562,7 +580,7 @@ async function alignTranslation(operationData) {
562
580
  text = subtitlesToText(fileContent);
563
581
  }
564
582
  else {
565
- throw new Error(`align only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
583
+ throw new Error(`align-translation only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
566
584
  }
567
585
  if (cliOptions.play == null) {
568
586
  cliOptions.play = outputFilenames.length === 0;
@@ -570,7 +588,7 @@ async function alignTranslation(operationData) {
570
588
  const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TranslationAlignmentOptions');
571
589
  const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
572
590
  const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true);
573
- const { timeline, wordTimeline, transcript, language, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.alignTranslation(audioFilename, text, options);
591
+ const { timeline, wordTimeline, translatedTranscript, sourceLanguage, targetLanguage, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.alignTranslation(audioFilename, text, options);
574
592
  if (outputFilenames.length > 0) {
575
593
  logger.start('\nWrite output files');
576
594
  }
@@ -579,9 +597,100 @@ async function alignTranslation(operationData) {
579
597
  const segmentEntry = timeline[segmentIndex];
580
598
  const segmentAudio = sliceRawAudioByTime(inputRawAudio, segmentEntry.startTime, segmentEntry.endTime);
581
599
  const sentenceTimeline = addTimeOffsetToTimeline(segmentEntry.timeline, -segmentEntry.startTime);
582
- await writeOutputFilesForSegment(outputFilenames, segmentIndex, timeline.length, segmentAudio, sentenceTimeline, segmentEntry.text, language, allowOverwrite);
600
+ await writeOutputFilesForSegment(outputFilenames, segmentIndex, timeline.length, segmentAudio, sentenceTimeline, segmentEntry.text, targetLanguage, allowOverwrite);
583
601
  }
584
602
  }
603
+ for (const outputFilename of outputFilenames) {
604
+ const partPatternMatch = outputFilename.match(filenamePlaceholderPattern);
605
+ if (partPatternMatch) {
606
+ continue;
607
+ }
608
+ const fileSaver = getFileSaver(outputFilename, allowOverwrite);
609
+ await fileSaver(inputRawAudio, timeline, translatedTranscript, options.subtitles);
610
+ await writeSourceSeparationOutputIfNeeded(outputFilename, isolatedRawAudio, backgroundRawAudio, allowOverwrite, true);
611
+ }
612
+ logger.end();
613
+ if (cliOptions.play) {
614
+ let audioToPlay;
615
+ if (isolatedRawAudio) {
616
+ audioToPlay = isolatedRawAudio;
617
+ }
618
+ else {
619
+ audioToPlay = inputRawAudio;
620
+ }
621
+ const normalizedAudioToPlay = normalizeAudioLevel(audioToPlay);
622
+ await playAudioWithWordTimeline(normalizedAudioToPlay, wordTimeline, translatedTranscript);
623
+ }
624
+ }
625
+ export async function alignTranscriptAndTranslation(operationData) {
626
+ const logger = new Logger();
627
+ const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
628
+ const audioFilename = operationArgs[0];
629
+ const outputFilenames = operationArgs.slice(3);
630
+ if (audioFilename == undefined) {
631
+ throw new Error(`align-transcript-and-translation requires a first argument containing the audio file path.`);
632
+ }
633
+ if (!existsSync(audioFilename)) {
634
+ throw new Error(`The given source file '${audioFilename}' was not found.`);
635
+ }
636
+ const nativeTranscriptFilePath = operationArgs[1];
637
+ if (nativeTranscriptFilePath == undefined) {
638
+ throw new Error(`align-transcript-and-translation requires a second argument containing the native language transcript file path.`);
639
+ }
640
+ if (!existsSync(nativeTranscriptFilePath)) {
641
+ throw new Error(`The given transcript file '${nativeTranscriptFilePath}' was not found.`);
642
+ }
643
+ const translatedTranscriptFilePath = operationArgs[2];
644
+ if (translatedTranscriptFilePath == undefined) {
645
+ throw new Error(`align-transcript-and-translation requires a third argument containing the translated language transcript file path.`);
646
+ }
647
+ if (!existsSync(translatedTranscriptFilePath)) {
648
+ throw new Error(`The given translated transcript file '${nativeTranscriptFilePath}' was not found.`);
649
+ }
650
+ let transcript;
651
+ {
652
+ const nativeTranscriptFileExtension = getLowercaseFileExtension(nativeTranscriptFilePath);
653
+ const fileContent = await readFile(nativeTranscriptFilePath, { encoding: 'utf-8' });
654
+ if (nativeTranscriptFileExtension == 'txt') {
655
+ transcript = fileContent;
656
+ }
657
+ else if (nativeTranscriptFileExtension == 'html' || nativeTranscriptFileExtension == 'htm') {
658
+ transcript = await convertHtmlToText(fileContent);
659
+ }
660
+ else if (nativeTranscriptFileExtension == 'srt' || nativeTranscriptFileExtension == 'vtt') {
661
+ transcript = subtitlesToText(fileContent);
662
+ }
663
+ else {
664
+ throw new Error(`align-transcript-and-translation only supports transcript files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
665
+ }
666
+ }
667
+ let translatedTranscript;
668
+ {
669
+ const translatedTranscriptFileExtension = getLowercaseFileExtension(translatedTranscriptFilePath);
670
+ const fileContent = await readFile(translatedTranscriptFilePath, { encoding: 'utf-8' });
671
+ if (translatedTranscriptFileExtension == 'txt') {
672
+ translatedTranscript = fileContent;
673
+ }
674
+ else if (translatedTranscriptFileExtension == 'html' || translatedTranscriptFileExtension == 'htm') {
675
+ translatedTranscript = await convertHtmlToText(fileContent);
676
+ }
677
+ else if (translatedTranscriptFileExtension == 'srt' || translatedTranscriptFileExtension == 'vtt') {
678
+ translatedTranscript = subtitlesToText(fileContent);
679
+ }
680
+ else {
681
+ throw new Error(`align-transcript-and-translation only supports transcript files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
682
+ }
683
+ }
684
+ if (cliOptions.play == null) {
685
+ cliOptions.play = outputFilenames.length === 0;
686
+ }
687
+ const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TranscriptAndTranslationAlignmentOptions');
688
+ const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
689
+ const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true);
690
+ const { timeline, wordTimeline, translatedTimeline, translatedWordTimeline, sourceLanguage, targetLanguage, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.alignTranscriptAndTranslation(audioFilename, transcript, translatedTranscript, options);
691
+ if (outputFilenames.length > 0) {
692
+ logger.start('\nWrite output files');
693
+ }
585
694
  for (const outputFilename of outputFilenames) {
586
695
  const partPatternMatch = outputFilename.match(filenamePlaceholderPattern);
587
696
  if (partPatternMatch) {
@@ -590,6 +699,13 @@ async function alignTranslation(operationData) {
590
699
  const fileSaver = getFileSaver(outputFilename, allowOverwrite);
591
700
  await fileSaver(inputRawAudio, timeline, transcript, options.subtitles);
592
701
  await writeSourceSeparationOutputIfNeeded(outputFilename, isolatedRawAudio, backgroundRawAudio, allowOverwrite, true);
702
+ const fileExtension = getLowercaseFileExtension(outputFilename);
703
+ if (['json', 'txt', 'srt', 'vtt'].includes(fileExtension)) {
704
+ const pathWithoutExtension = outputFilename.substring(0, outputFilename.lastIndexOf('.'));
705
+ const translatedOutputPath = `${pathWithoutExtension}.translated.${fileExtension}`;
706
+ const translatedFileSaver = getFileSaver(translatedOutputPath, allowOverwrite);
707
+ await translatedFileSaver(inputRawAudio, translatedTimeline, translatedTranscript, options.subtitles);
708
+ }
593
709
  }
594
710
  logger.end();
595
711
  if (cliOptions.play) {
@@ -601,10 +717,118 @@ async function alignTranslation(operationData) {
601
717
  audioToPlay = inputRawAudio;
602
718
  }
603
719
  const normalizedAudioToPlay = normalizeAudioLevel(audioToPlay);
604
- await playAudioWithWordTimeline(normalizedAudioToPlay, wordTimeline, transcript);
720
+ await playAudioWithWordTimeline(normalizedAudioToPlay, translatedWordTimeline, translatedTranscript);
605
721
  }
606
722
  }
607
- async function translateSpeech(operationData) {
723
+ export async function alignTimelineTranslation(operationData) {
724
+ const logger = new Logger();
725
+ const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
726
+ const timelineFilename = operationArgs[0];
727
+ const outputFilenames = operationArgs.slice(2);
728
+ if (timelineFilename == undefined) {
729
+ throw new Error(`align-timeline-translation requires a first argument containing the timeline file path.`);
730
+ }
731
+ if (getLowercaseFileExtension(timelineFilename) != 'json') {
732
+ throw new Error(`align-timeline-translation only supports timeline files with extension 'json'`);
733
+ }
734
+ if (!existsSync(timelineFilename)) {
735
+ throw new Error(`The given timeline file '${timelineFilename}' was not found.`);
736
+ }
737
+ const timeline = await readAndParseJsonFile(timelineFilename);
738
+ const translationFilePath = operationArgs[1];
739
+ if (translationFilePath == undefined) {
740
+ throw new Error(`align-timeline-translation requires a second argument containing the translated reference file path.`);
741
+ }
742
+ if (!existsSync(translationFilePath)) {
743
+ throw new Error(`The given reference file '${translationFilePath}' was not found.`);
744
+ }
745
+ const translationFileExtension = getLowercaseFileExtension(translationFilePath);
746
+ const translationFileContent = await readFile(translationFilePath, { encoding: 'utf-8' });
747
+ let translationText;
748
+ if (translationFileExtension == 'txt') {
749
+ translationText = translationFileContent;
750
+ }
751
+ else if (translationFileExtension == 'html' || translationFileExtension == 'htm') {
752
+ translationText = await convertHtmlToText(translationFileContent);
753
+ }
754
+ else if (translationFileExtension == 'srt' || translationFileExtension == 'vtt') {
755
+ translationText = subtitlesToText(translationFileContent);
756
+ }
757
+ else {
758
+ throw new Error(`align only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
759
+ }
760
+ const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TimelineTranslationAlignmentOptions');
761
+ const { timeline: translationTimeline, wordTimeline: translationWordTimeline, rawAudio } = await API.alignTimelineTranslation(timeline, translationText, options);
762
+ if (outputFilenames.length > 0) {
763
+ logger.start('\nWrite output files');
764
+ }
765
+ const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
766
+ for (const outputFilename of outputFilenames) {
767
+ const partPatternMatch = outputFilename.match(filenamePlaceholderPattern);
768
+ if (partPatternMatch) {
769
+ continue;
770
+ }
771
+ const fileSaver = getFileSaver(outputFilename, allowOverwrite);
772
+ await fileSaver(getEmptyRawAudio(1, 16000), translationTimeline, translationText, options.subtitles);
773
+ }
774
+ logger.end();
775
+ if (cliOptions.play && rawAudio) {
776
+ const normalizedAudioToPlay = normalizeAudioLevel(rawAudio);
777
+ let transcriptToPlay;
778
+ let timelineToPlay;
779
+ transcriptToPlay = translationText;
780
+ timelineToPlay = translationWordTimeline;
781
+ await playAudioWithWordTimeline(normalizedAudioToPlay, timelineToPlay, transcriptToPlay);
782
+ }
783
+ }
784
+ export async function translateText(operationData) {
785
+ const logger = new Logger();
786
+ const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
787
+ const inputFilename = operationArgs[0];
788
+ const outputFilenames = operationArgs.slice(1);
789
+ if (inputFilename == undefined) {
790
+ throw new Error(`translate-text requires an argument containing the input file path.`);
791
+ }
792
+ if (!existsSync(inputFilename)) {
793
+ throw new Error(`The given input file '${inputFilename}' was not found.`);
794
+ }
795
+ const inputFileExtension = getLowercaseFileExtension(inputFilename);
796
+ const inputFileContent = await readFile(inputFilename, { encoding: 'utf-8' });
797
+ let inputText;
798
+ if (inputFileExtension === 'txt') {
799
+ inputText = inputFileContent;
800
+ }
801
+ else if (inputFileExtension === 'html' || inputFileExtension === 'htm') {
802
+ inputText = await convertHtmlToText(inputFileContent);
803
+ }
804
+ else if (inputFileExtension == 'srt' || inputFileExtension == 'vtt') {
805
+ inputText = subtitlesToText(inputFileContent);
806
+ }
807
+ else {
808
+ throw new Error(`align only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
809
+ }
810
+ const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TextTranslationOptions');
811
+ const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
812
+ await checkOutputFilenames(outputFilenames, false, true, true);
813
+ const { text, translatedText, translationPairs, sourceLanguage, targetLanguage, } = await API.translateText(inputFileContent, options);
814
+ if (outputFilenames.length > 0) {
815
+ logger.start('\nWrite output files');
816
+ for (const outputFilename of outputFilenames) {
817
+ const partPatternMatch = outputFilename.match(filenamePlaceholderPattern);
818
+ if (partPatternMatch) {
819
+ continue;
820
+ }
821
+ const fileSaver = getFileSaver(outputFilename, allowOverwrite);
822
+ await fileSaver(getEmptyRawAudio(1, 16000), translationPairs, translatedText, undefined);
823
+ }
824
+ logger.end();
825
+ }
826
+ else {
827
+ logger.log(``);
828
+ logger.log(translatedText);
829
+ }
830
+ }
831
+ export async function translateSpeech(operationData) {
608
832
  const logger = new Logger();
609
833
  const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
610
834
  const inputFilename = operationArgs[0];
@@ -667,7 +891,7 @@ async function translateSpeech(operationData) {
667
891
  await playAudioWithWordTimeline(normalizedAudioToPlay, timelineToPlay, transcriptToPlay);
668
892
  }
669
893
  }
670
- async function detectLanguage(operationData, mode) {
894
+ export async function detectLanguage(operationData, mode) {
671
895
  const logger = new Logger();
672
896
  const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
673
897
  const inputFilePath = operationArgs[0];
@@ -721,7 +945,7 @@ async function detectLanguage(operationData, mode) {
721
945
  }
722
946
  logger.end();
723
947
  }
724
- async function detectVoiceActivity(operationData) {
948
+ export async function detectVoiceActivity(operationData) {
725
949
  const logger = new Logger();
726
950
  const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
727
951
  const audioFilename = operationArgs[0];
@@ -766,7 +990,7 @@ async function detectVoiceActivity(operationData) {
766
990
  await playAudioWithWordTimeline(normalizedAudio, timelineToPlay);
767
991
  }
768
992
  }
769
- async function denoise(operationData) {
993
+ export async function denoise(operationData) {
770
994
  const logger = new Logger();
771
995
  const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
772
996
  const audioFilename = operationArgs[0];
@@ -796,7 +1020,7 @@ async function denoise(operationData) {
796
1020
  await playAudioSamples(denoisedAudio);
797
1021
  }
798
1022
  }
799
- async function isolate(operationData) {
1023
+ export async function isolate(operationData) {
800
1024
  const logger = new Logger();
801
1025
  const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
802
1026
  const audioFilename = operationArgs[0];
@@ -825,7 +1049,7 @@ async function isolate(operationData) {
825
1049
  await playAudioSamples(isolatedRawAudio);
826
1050
  }
827
1051
  }
828
- async function listEngines(operationData) {
1052
+ export async function listEngines(operationData) {
829
1053
  const logger = new Logger();
830
1054
  const { operationArgs } = operationData;
831
1055
  const targetOperation = operationArgs[0];
@@ -857,6 +1081,10 @@ async function listEngines(operationData) {
857
1081
  engines = API.translationAlignmentEngines;
858
1082
  break;
859
1083
  }
1084
+ case 'translate-text': {
1085
+ engines = API.textTranslationEngines;
1086
+ break;
1087
+ }
860
1088
  case 'translate-speech': {
861
1089
  engines = API.speechTranslationEngines;
862
1090
  break;
@@ -892,7 +1120,7 @@ async function listEngines(operationData) {
892
1120
  throw new Error(`The operation '${targetOperation}' is not associated with a list of engines.`);
893
1121
  }
894
1122
  default: {
895
- throw new Error(`Unrecognized operation: '${targetOperation}'`);
1123
+ throw new Error(`Unrecognized operation name: '${targetOperation}'`);
896
1124
  }
897
1125
  }
898
1126
  for (const [index, engine] of engines.entries()) {
@@ -905,7 +1133,7 @@ async function listEngines(operationData) {
905
1133
  }
906
1134
  }
907
1135
  }
908
- async function listTTSVoices(operationData) {
1136
+ export async function listTTSVoices(operationData) {
909
1137
  const logger = new Logger();
910
1138
  const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
911
1139
  const targetEngine = operationArgs[0];
@@ -947,7 +1175,7 @@ async function listTTSVoices(operationData) {
947
1175
  }
948
1176
  logger.end();
949
1177
  }
950
- async function installPackages(operationData) {
1178
+ export async function installPackages(operationData) {
951
1179
  const logger = new Logger();
952
1180
  const { operationArgs } = operationData;
953
1181
  if (operationArgs.length == 0) {
@@ -973,7 +1201,7 @@ async function installPackages(operationData) {
973
1201
  }
974
1202
  }
975
1203
  }
976
- async function uninstallPackages(operationData) {
1204
+ export async function uninstallPackages(operationData) {
977
1205
  const logger = new Logger();
978
1206
  const { operationArgs } = operationData;
979
1207
  if (operationArgs.length == 0) {
@@ -994,7 +1222,7 @@ async function uninstallPackages(operationData) {
994
1222
  logger.log(`The packages ${failedPackageNames.join(', ')} failed to uninstall`, 'error');
995
1223
  }
996
1224
  }
997
- async function listPackages(operationData) {
1225
+ export async function listPackages(operationData) {
998
1226
  const logger = new Logger();
999
1227
  const packagesDir = await ensureAndGetPackagesDir();
1000
1228
  const installedPackageNames = await readdir(packagesDir);
@@ -1017,7 +1245,7 @@ async function listPackages(operationData) {
1017
1245
  logger.log(``);
1018
1246
  logger.log(installedPackageNamesFormatted.join('\n'));
1019
1247
  }
1020
- async function serve(operationData) {
1248
+ export async function serve(operationData) {
1021
1249
  const { operationOptionsLookup } = operationData;
1022
1250
  const options = await optionsLookupToTypedObject(operationOptionsLookup, 'ServerOptions');
1023
1251
  async function onServerStarted(serverOptions) {
@@ -1143,13 +1371,13 @@ async function optionsLookupToTypedObject(cliOptionsMap, optionsRoot, additional
1143
1371
  return resultingObj;
1144
1372
  }
1145
1373
  let cachedOptionsSchema;
1146
- export async function getOptionsSchema() {
1374
+ async function getOptionsSchema() {
1147
1375
  if (!cachedOptionsSchema) {
1148
1376
  cachedOptionsSchema = await readAndParseJsonFile(resolveToModuleRootDir('data/schemas/options.json'));
1149
1377
  }
1150
1378
  return cachedOptionsSchema;
1151
1379
  }
1152
- export async function checkOutputFilenames(outputFilenames, acceptMediaOutputs, acceptMetadataOutputs, acceptSubtitleOutputs) {
1380
+ async function checkOutputFilenames(outputFilenames, acceptMediaOutputs, acceptMetadataOutputs, acceptSubtitleOutputs) {
1153
1381
  const supportedFileExtensions = [];
1154
1382
  if (acceptMediaOutputs) {
1155
1383
  supportedFileExtensions.push(...supportedOutputMediaFileExtensions);
@@ -1188,7 +1416,7 @@ export async function checkOutputFilenames(outputFilenames, acceptMediaOutputs,
1188
1416
  }
1189
1417
  return { includesPlaceholderPattern };
1190
1418
  }
1191
- export async function writeOutputFilesForSegment(outputFilenames, index, total, audio, timeline, text, language, allowOverwrite) {
1419
+ async function writeOutputFilesForSegment(outputFilenames, index, total, audio, timeline, text, language, allowOverwrite) {
1192
1420
  const digitCount = Math.max((total + 1).toString().length, 2);
1193
1421
  const segmentWords = (await splitToWords(text, language)).filter(text => wordCharacterPattern.test(text));
1194
1422
  const segmentJoinedWords = segmentWords.join(' ').trim();