echogarden 1.4.3 → 1.5.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.
- package/README.md +1 -1
- package/data/schemas/options.json +267 -19
- package/data/tables/lcid-table.json +9 -0
- package/dist/alignment/DTWMfccSequenceAlignment.d.ts +1 -1
- package/dist/alignment/DTWMfccSequenceAlignment.js +9 -5
- package/dist/alignment/DTWMfccSequenceAlignment.js.map +1 -1
- package/dist/alignment/DTWSequenceAlignmentWindowed.js +4 -4
- package/dist/alignment/DTWSequenceAlignmentWindowed.js.map +1 -1
- package/dist/alignment/{TextAlignment.d.ts → SemanticTextAlignment.d.ts} +10 -1
- package/dist/alignment/SemanticTextAlignment.js +336 -0
- package/dist/alignment/SemanticTextAlignment.js.map +1 -0
- package/dist/alignment/SpeechAlignment.d.ts +2 -1
- package/dist/alignment/SpeechAlignment.js +106 -38
- package/dist/alignment/SpeechAlignment.js.map +1 -1
- package/dist/api/API.d.ts +7 -2
- package/dist/api/API.js +7 -2
- package/dist/api/API.js.map +1 -1
- package/dist/api/APIOptions.d.ts +4 -1
- package/dist/api/Alignment.d.ts +1 -1
- package/dist/api/Alignment.js +14 -6
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/LanguageDetectionCommon.d.ts +6 -0
- package/dist/api/LanguageDetectionCommon.js +2 -0
- package/dist/api/LanguageDetectionCommon.js.map +1 -0
- package/dist/api/Recognition.js.map +1 -1
- package/dist/api/{LanguageDetection.d.ts → SpeechLanguageDetection.d.ts} +1 -25
- package/dist/api/{LanguageDetection.js → SpeechLanguageDetection.js} +1 -68
- package/dist/api/SpeechLanguageDetection.js.map +1 -0
- package/dist/api/{Translation.js → SpeechTranslation.js} +3 -3
- package/dist/api/SpeechTranslation.js.map +1 -0
- package/dist/api/Synthesis.js +4 -4
- package/dist/api/TextLanguageDetection.d.ts +21 -0
- package/dist/api/TextLanguageDetection.js +67 -0
- package/dist/api/TextLanguageDetection.js.map +1 -0
- package/dist/api/TextTranslation.d.ts +5 -0
- package/dist/api/TextTranslation.js +4 -0
- package/dist/api/TextTranslation.js.map +1 -0
- package/dist/api/TimelineTranslationAlignment.d.ts +23 -0
- package/dist/api/TimelineTranslationAlignment.js +92 -0
- package/dist/api/TimelineTranslationAlignment.js.map +1 -0
- package/dist/api/TranscriptAndTranslationAlignment.d.ts +35 -0
- package/dist/api/TranscriptAndTranslationAlignment.js +78 -0
- package/dist/api/TranscriptAndTranslationAlignment.js.map +1 -0
- package/dist/api/TranslationAlignment.d.ts +4 -3
- package/dist/api/TranslationAlignment.js +9 -8
- package/dist/api/TranslationAlignment.js.map +1 -1
- package/dist/api/VoiceActivityDetection.js +16 -1
- package/dist/api/VoiceActivityDetection.js.map +1 -1
- package/dist/cli/CLI.d.ts +27 -7
- package/dist/cli/CLI.js +205 -34
- package/dist/cli/CLI.js.map +1 -1
- package/dist/codecs/FFMpegTranscoder.js +7 -0
- package/dist/codecs/FFMpegTranscoder.js.map +1 -1
- package/dist/dsp/FFT.d.ts +1 -1
- package/dist/dsp/FFT.js +6 -0
- package/dist/dsp/FFT.js.map +1 -1
- package/dist/dsp/KWeightingFilter.js +1 -1
- package/dist/dsp/KWeightingFilter.js.map +1 -1
- package/dist/dsp/MelSpectogram.d.ts +3 -2
- package/dist/dsp/MelSpectogram.js +14 -8
- package/dist/dsp/MelSpectogram.js.map +1 -1
- package/dist/math/VectorMath.d.ts +22 -20
- package/dist/math/VectorMath.js +57 -30
- package/dist/math/VectorMath.js.map +1 -1
- package/dist/recognition/WhisperCppSTT.d.ts +1 -1
- package/dist/recognition/WhisperCppSTT.js +2 -2
- package/dist/recognition/WhisperCppSTT.js.map +1 -1
- package/dist/recognition/WhisperSTT.js +9 -6
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/server/Client.d.ts +3 -2
- package/dist/server/Client.js.map +1 -1
- package/dist/server/Worker.d.ts +3 -2
- package/dist/server/Worker.js +3 -2
- package/dist/server/Worker.js.map +1 -1
- package/dist/source-separation/MDXNetSourceSeparation.js +1 -1
- package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
- package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.d.ts +13 -0
- package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js +68 -0
- package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js.map +1 -0
- package/dist/speech-language-detection/SileroLanguageDetection.d.ts +1 -1
- package/dist/speech-language-detection/SileroLanguageDetection.js +1 -1
- package/dist/speech-language-detection/SileroLanguageDetection.js.map +1 -1
- package/dist/synthesis/EspeakTTS.js +4 -5
- package/dist/synthesis/EspeakTTS.js.map +1 -1
- package/dist/tests/Test.js +0 -8
- package/dist/tests/Test.js.map +1 -1
- package/dist/text-language-detection/FastTextLanguageDetection.d.ts +1 -1
- package/dist/text-language-detection/FastTextLanguageDetection.js.map +1 -1
- package/dist/text-language-detection/TinyLDLanguageDetection.d.ts +1 -1
- package/dist/text-language-detection/TinyLDLanguageDetection.js.map +1 -1
- package/dist/text-translation/NLLBTextTranslation.js +1 -1
- package/dist/text-translation/NLLBTextTranslation.js.map +1 -1
- package/dist/utilities/Locale.d.ts +1 -1
- package/dist/utilities/Locale.js +1 -1
- package/dist/utilities/OnnxUtilities.d.ts +1 -1
- package/dist/utilities/PackageManager.js +8 -2
- package/dist/utilities/PackageManager.js.map +1 -1
- package/dist/utilities/Timeline.d.ts +1 -0
- package/dist/utilities/Timeline.js +12 -0
- package/dist/utilities/Timeline.js.map +1 -1
- package/docs/API.md +81 -3
- package/docs/CLI.md +51 -1
- package/docs/Engines.md +24 -0
- package/docs/Options.md +33 -1
- package/docs/Tasklist.md +4 -1
- package/package.json +11 -11
- package/src/alignment/DTWMfccSequenceAlignment.ts +11 -5
- package/src/alignment/DTWSequenceAlignmentWindowed.ts +4 -4
- package/src/alignment/SemanticTextAlignment.ts +467 -0
- package/src/alignment/SpeechAlignment.ts +180 -53
- package/src/api/API.ts +18 -2
- package/src/api/APIOptions.ts +14 -1
- package/src/api/Alignment.ts +32 -10
- package/src/api/LanguageDetectionCommon.ts +7 -0
- package/src/api/Recognition.ts +2 -0
- package/src/api/{LanguageDetection.ts → SpeechLanguageDetection.ts} +1 -119
- package/src/api/{Translation.ts → SpeechTranslation.ts} +2 -2
- package/src/api/Synthesis.ts +4 -4
- package/src/api/TextLanguageDetection.ts +116 -0
- package/src/api/TextTranslation.ts +9 -0
- package/src/api/TimelineTranslationAlignment.ts +162 -0
- package/src/api/TranscriptAndTranslationAlignment.ts +164 -0
- package/src/api/TranslationAlignment.ts +12 -10
- package/src/api/VoiceActivityDetection.ts +24 -3
- package/src/cli/CLI.ts +276 -34
- package/src/codecs/FFMpegTranscoder.ts +6 -0
- package/src/dsp/FFT.ts +8 -2
- package/src/dsp/KWeightingFilter.ts +1 -1
- package/src/dsp/MelSpectogram.ts +17 -8
- package/src/math/VectorMath.ts +87 -52
- package/src/recognition/WhisperCppSTT.ts +2 -2
- package/src/recognition/WhisperSTT.ts +10 -6
- package/src/server/Client.ts +3 -2
- package/src/server/Worker.ts +3 -2
- package/src/source-separation/MDXNetSourceSeparation.ts +1 -1
- package/src/speech-embeddings/WavToVec2BertFeatureEmbeddings.ts +107 -0
- package/src/speech-language-detection/SileroLanguageDetection.ts +2 -1
- package/src/synthesis/EspeakTTS.ts +6 -8
- package/src/tests/Test.ts +1 -12
- package/src/text-language-detection/FastTextLanguageDetection.ts +1 -1
- package/src/text-language-detection/TinyLDLanguageDetection.ts +1 -1
- package/src/text-translation/NLLBTextTranslation.ts +1 -1
- package/src/utilities/Locale.ts +1 -1
- package/src/utilities/OnnxUtilities.ts +1 -1
- package/src/utilities/PackageManager.ts +9 -2
- package/src/utilities/Timeline.ts +14 -0
- package/dist/alignment/TextAlignment.js +0 -63
- package/dist/alignment/TextAlignment.js.map +0 -1
- package/dist/api/LanguageDetection.js.map +0 -1
- package/dist/api/Translation.js.map +0 -1
- package/src/alignment/TextAlignment.ts +0 -96
- /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,
|
|
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,17 +170,21 @@ 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
|
|
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
|
|
177
|
-
` Align audio file to
|
|
178
|
-
`${executableName} ${chalk.magentaBright('translate-speech')}
|
|
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-speech')} audioFile [output files...] [options...]`,
|
|
179
179
|
` Transcribe audio file directly to a different language\n`,
|
|
180
|
-
`${executableName} ${chalk.magentaBright('align-translation')} audioFile
|
|
181
|
-
` Align audio file to
|
|
180
|
+
`${executableName} ${chalk.magentaBright('align-translation')} audioFile translatedTranscriptFile [output files...] [options...]`,
|
|
181
|
+
` Align spoken audio file to its translated transcript\n`,
|
|
182
|
+
`${executableName} ${chalk.magentaBright('align-transcript-and-translation')} audioFile transcriptFile translatedTranscriptFile [output files...] [options...]`,
|
|
183
|
+
` Align spoken audio file to both its transcript and its translated transcript using a two-stage approach.\n`,
|
|
184
|
+
`${executableName} ${chalk.magentaBright('align-timeline-translation')} timelineFile translatedFile [output files...] [options...]`,
|
|
185
|
+
` Align a given timeline file to its translated text\n`,
|
|
182
186
|
`${executableName} ${chalk.magentaBright('detect-speech-language')} audioFile [output files...] [options...]`,
|
|
183
|
-
` Detect language of audio file\n`,
|
|
187
|
+
` Detect language of spoken audio file\n`,
|
|
184
188
|
`${executableName} ${chalk.magentaBright('detect-text-language')} inputFile [output files...] [options...]`,
|
|
185
189
|
` Detect language of textual file\n`,
|
|
186
190
|
`${executableName} ${chalk.magentaBright('detect-voice-activity')} audioFile [output files...] [options...]`,
|
|
@@ -229,6 +233,14 @@ async function startWithArgs(operationData) {
|
|
|
229
233
|
await alignTranslation(operationData);
|
|
230
234
|
break;
|
|
231
235
|
}
|
|
236
|
+
case 'align-transcript-and-translation': {
|
|
237
|
+
await alignTranscriptAndTranslation(operationData);
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
case 'align-timeline-translation': {
|
|
241
|
+
await alignTimelineTranslation(operationData);
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
232
244
|
case 'detect-language': {
|
|
233
245
|
await detectLanguage(operationData, 'auto');
|
|
234
246
|
break;
|
|
@@ -283,7 +295,7 @@ async function startWithArgs(operationData) {
|
|
|
283
295
|
}
|
|
284
296
|
}
|
|
285
297
|
}
|
|
286
|
-
async function speak(operationData) {
|
|
298
|
+
export async function speak(operationData) {
|
|
287
299
|
const logger = new Logger();
|
|
288
300
|
const { operationArgs, operation, operationOptionsLookup, cliOptions } = operationData;
|
|
289
301
|
const mainArg = operationArgs[0];
|
|
@@ -345,8 +357,8 @@ async function speak(operationData) {
|
|
|
345
357
|
}
|
|
346
358
|
else if (sourceFileExtension == 'srt' || sourceFileExtension == 'vtt') {
|
|
347
359
|
const fileContent = await readFile(sourceFile, { encoding: 'utf-8' });
|
|
348
|
-
textSegments = subtitlesToTimeline(fileContent).map(entry => entry.text)
|
|
349
|
-
|
|
360
|
+
//textSegments = subtitlesToTimeline(fileContent).map(entry => entry.text)
|
|
361
|
+
textSegments = [subtitlesToText(fileContent)];
|
|
350
362
|
}
|
|
351
363
|
else if (sourceFileExtension == 'xml' || sourceFileExtension == 'ssml') {
|
|
352
364
|
options.ssml = true;
|
|
@@ -415,7 +427,7 @@ async function speak(operationData) {
|
|
|
415
427
|
}
|
|
416
428
|
logger.end();
|
|
417
429
|
}
|
|
418
|
-
async function transcribe(operationData) {
|
|
430
|
+
export async function transcribe(operationData) {
|
|
419
431
|
const logger = new Logger();
|
|
420
432
|
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
421
433
|
const sourceFilename = operationArgs[0];
|
|
@@ -458,7 +470,7 @@ async function transcribe(operationData) {
|
|
|
458
470
|
await playAudioWithWordTimeline(normalizedAudioToPlay, wordTimeline, transcript);
|
|
459
471
|
}
|
|
460
472
|
}
|
|
461
|
-
async function align(operationData) {
|
|
473
|
+
export async function align(operationData) {
|
|
462
474
|
const logger = new Logger();
|
|
463
475
|
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
464
476
|
const audioFilename = operationArgs[0];
|
|
@@ -531,13 +543,13 @@ async function align(operationData) {
|
|
|
531
543
|
await playAudioWithWordTimeline(normalizedAudioToPlay, wordTimeline, transcript);
|
|
532
544
|
}
|
|
533
545
|
}
|
|
534
|
-
async function alignTranslation(operationData) {
|
|
546
|
+
export async function alignTranslation(operationData) {
|
|
535
547
|
const logger = new Logger();
|
|
536
548
|
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
537
549
|
const audioFilename = operationArgs[0];
|
|
538
550
|
const outputFilenames = operationArgs.slice(2);
|
|
539
551
|
if (audioFilename == undefined) {
|
|
540
|
-
throw new Error(`align-translation requires
|
|
552
|
+
throw new Error(`align-translation requires a first argument containing the audio file path.`);
|
|
541
553
|
}
|
|
542
554
|
if (!existsSync(audioFilename)) {
|
|
543
555
|
throw new Error(`The given source file '${audioFilename}' was not found.`);
|
|
@@ -562,7 +574,7 @@ async function alignTranslation(operationData) {
|
|
|
562
574
|
text = subtitlesToText(fileContent);
|
|
563
575
|
}
|
|
564
576
|
else {
|
|
565
|
-
throw new Error(`align only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
|
|
577
|
+
throw new Error(`align-translation only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
|
|
566
578
|
}
|
|
567
579
|
if (cliOptions.play == null) {
|
|
568
580
|
cliOptions.play = outputFilenames.length === 0;
|
|
@@ -570,7 +582,7 @@ async function alignTranslation(operationData) {
|
|
|
570
582
|
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TranslationAlignmentOptions');
|
|
571
583
|
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
572
584
|
const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true);
|
|
573
|
-
const { timeline, wordTimeline,
|
|
585
|
+
const { timeline, wordTimeline, translatedTranscript, sourceLanguage, targetLanguage, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.alignTranslation(audioFilename, text, options);
|
|
574
586
|
if (outputFilenames.length > 0) {
|
|
575
587
|
logger.start('\nWrite output files');
|
|
576
588
|
}
|
|
@@ -579,9 +591,100 @@ async function alignTranslation(operationData) {
|
|
|
579
591
|
const segmentEntry = timeline[segmentIndex];
|
|
580
592
|
const segmentAudio = sliceRawAudioByTime(inputRawAudio, segmentEntry.startTime, segmentEntry.endTime);
|
|
581
593
|
const sentenceTimeline = addTimeOffsetToTimeline(segmentEntry.timeline, -segmentEntry.startTime);
|
|
582
|
-
await writeOutputFilesForSegment(outputFilenames, segmentIndex, timeline.length, segmentAudio, sentenceTimeline, segmentEntry.text,
|
|
594
|
+
await writeOutputFilesForSegment(outputFilenames, segmentIndex, timeline.length, segmentAudio, sentenceTimeline, segmentEntry.text, targetLanguage, allowOverwrite);
|
|
583
595
|
}
|
|
584
596
|
}
|
|
597
|
+
for (const outputFilename of outputFilenames) {
|
|
598
|
+
const partPatternMatch = outputFilename.match(filenamePlaceholderPattern);
|
|
599
|
+
if (partPatternMatch) {
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
const fileSaver = getFileSaver(outputFilename, allowOverwrite);
|
|
603
|
+
await fileSaver(inputRawAudio, timeline, translatedTranscript, options.subtitles);
|
|
604
|
+
await writeSourceSeparationOutputIfNeeded(outputFilename, isolatedRawAudio, backgroundRawAudio, allowOverwrite, true);
|
|
605
|
+
}
|
|
606
|
+
logger.end();
|
|
607
|
+
if (cliOptions.play) {
|
|
608
|
+
let audioToPlay;
|
|
609
|
+
if (isolatedRawAudio) {
|
|
610
|
+
audioToPlay = isolatedRawAudio;
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
audioToPlay = inputRawAudio;
|
|
614
|
+
}
|
|
615
|
+
const normalizedAudioToPlay = normalizeAudioLevel(audioToPlay);
|
|
616
|
+
await playAudioWithWordTimeline(normalizedAudioToPlay, wordTimeline, translatedTranscript);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
export async function alignTranscriptAndTranslation(operationData) {
|
|
620
|
+
const logger = new Logger();
|
|
621
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
622
|
+
const audioFilename = operationArgs[0];
|
|
623
|
+
const outputFilenames = operationArgs.slice(3);
|
|
624
|
+
if (audioFilename == undefined) {
|
|
625
|
+
throw new Error(`align-transcript-and-translation requires a first argument containing the audio file path.`);
|
|
626
|
+
}
|
|
627
|
+
if (!existsSync(audioFilename)) {
|
|
628
|
+
throw new Error(`The given source file '${audioFilename}' was not found.`);
|
|
629
|
+
}
|
|
630
|
+
const nativeTranscriptFilePath = operationArgs[1];
|
|
631
|
+
if (nativeTranscriptFilePath == undefined) {
|
|
632
|
+
throw new Error(`align-transcript-and-translation requires a second argument containing the native language transcript file path.`);
|
|
633
|
+
}
|
|
634
|
+
if (!existsSync(nativeTranscriptFilePath)) {
|
|
635
|
+
throw new Error(`The given transcript file '${nativeTranscriptFilePath}' was not found.`);
|
|
636
|
+
}
|
|
637
|
+
const translatedTranscriptFilePath = operationArgs[2];
|
|
638
|
+
if (translatedTranscriptFilePath == undefined) {
|
|
639
|
+
throw new Error(`align-transcript-and-translation requires a third argument containing the translated language transcript file path.`);
|
|
640
|
+
}
|
|
641
|
+
if (!existsSync(translatedTranscriptFilePath)) {
|
|
642
|
+
throw new Error(`The given translated transcript file '${nativeTranscriptFilePath}' was not found.`);
|
|
643
|
+
}
|
|
644
|
+
let transcript;
|
|
645
|
+
{
|
|
646
|
+
const nativeTranscriptFileExtension = getLowercaseFileExtension(nativeTranscriptFilePath);
|
|
647
|
+
const fileContent = await readFile(nativeTranscriptFilePath, { encoding: 'utf-8' });
|
|
648
|
+
if (nativeTranscriptFileExtension == 'txt') {
|
|
649
|
+
transcript = fileContent;
|
|
650
|
+
}
|
|
651
|
+
else if (nativeTranscriptFileExtension == 'html' || nativeTranscriptFileExtension == 'htm') {
|
|
652
|
+
transcript = await convertHtmlToText(fileContent);
|
|
653
|
+
}
|
|
654
|
+
else if (nativeTranscriptFileExtension == 'srt' || nativeTranscriptFileExtension == 'vtt') {
|
|
655
|
+
transcript = subtitlesToText(fileContent);
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
throw new Error(`align-transcript-and-translation only supports transcript files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
let translatedTranscript;
|
|
662
|
+
{
|
|
663
|
+
const translatedTranscriptFileExtension = getLowercaseFileExtension(translatedTranscriptFilePath);
|
|
664
|
+
const fileContent = await readFile(translatedTranscriptFilePath, { encoding: 'utf-8' });
|
|
665
|
+
if (translatedTranscriptFileExtension == 'txt') {
|
|
666
|
+
translatedTranscript = fileContent;
|
|
667
|
+
}
|
|
668
|
+
else if (translatedTranscriptFileExtension == 'html' || translatedTranscriptFileExtension == 'htm') {
|
|
669
|
+
translatedTranscript = await convertHtmlToText(fileContent);
|
|
670
|
+
}
|
|
671
|
+
else if (translatedTranscriptFileExtension == 'srt' || translatedTranscriptFileExtension == 'vtt') {
|
|
672
|
+
translatedTranscript = subtitlesToText(fileContent);
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
throw new Error(`align-transcript-and-translation only supports transcript files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
if (cliOptions.play == null) {
|
|
679
|
+
cliOptions.play = outputFilenames.length === 0;
|
|
680
|
+
}
|
|
681
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TranscriptAndTranslationAlignmentOptions');
|
|
682
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
683
|
+
const { includesPlaceholderPattern } = await checkOutputFilenames(outputFilenames, true, true, true);
|
|
684
|
+
const { timeline, wordTimeline, translatedTimeline, translatedWordTimeline, sourceLanguage, targetLanguage, inputRawAudio, isolatedRawAudio, backgroundRawAudio } = await API.alignTranscriptAndTranslation(audioFilename, transcript, translatedTranscript, options);
|
|
685
|
+
if (outputFilenames.length > 0) {
|
|
686
|
+
logger.start('\nWrite output files');
|
|
687
|
+
}
|
|
585
688
|
for (const outputFilename of outputFilenames) {
|
|
586
689
|
const partPatternMatch = outputFilename.match(filenamePlaceholderPattern);
|
|
587
690
|
if (partPatternMatch) {
|
|
@@ -590,6 +693,13 @@ async function alignTranslation(operationData) {
|
|
|
590
693
|
const fileSaver = getFileSaver(outputFilename, allowOverwrite);
|
|
591
694
|
await fileSaver(inputRawAudio, timeline, transcript, options.subtitles);
|
|
592
695
|
await writeSourceSeparationOutputIfNeeded(outputFilename, isolatedRawAudio, backgroundRawAudio, allowOverwrite, true);
|
|
696
|
+
const fileExtension = getLowercaseFileExtension(outputFilename);
|
|
697
|
+
if (['json', 'txt', 'srt', 'vtt'].includes(fileExtension)) {
|
|
698
|
+
const pathWithoutExtension = outputFilename.substring(0, outputFilename.lastIndexOf('.'));
|
|
699
|
+
const translatedOutputPath = `${pathWithoutExtension}.translated.${fileExtension}`;
|
|
700
|
+
const translatedFileSaver = getFileSaver(translatedOutputPath, allowOverwrite);
|
|
701
|
+
await translatedFileSaver(inputRawAudio, translatedTimeline, translatedTranscript, options.subtitles);
|
|
702
|
+
}
|
|
593
703
|
}
|
|
594
704
|
logger.end();
|
|
595
705
|
if (cliOptions.play) {
|
|
@@ -601,10 +711,71 @@ async function alignTranslation(operationData) {
|
|
|
601
711
|
audioToPlay = inputRawAudio;
|
|
602
712
|
}
|
|
603
713
|
const normalizedAudioToPlay = normalizeAudioLevel(audioToPlay);
|
|
604
|
-
await playAudioWithWordTimeline(normalizedAudioToPlay,
|
|
714
|
+
await playAudioWithWordTimeline(normalizedAudioToPlay, translatedWordTimeline, translatedTranscript);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
export async function alignTimelineTranslation(operationData) {
|
|
718
|
+
const logger = new Logger();
|
|
719
|
+
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
720
|
+
const timelineFilename = operationArgs[0];
|
|
721
|
+
const outputFilenames = operationArgs.slice(2);
|
|
722
|
+
if (timelineFilename == undefined) {
|
|
723
|
+
throw new Error(`align-timeline-translation requires a first argument containing the timeline file path.`);
|
|
724
|
+
}
|
|
725
|
+
if (getLowercaseFileExtension(timelineFilename) != 'json') {
|
|
726
|
+
throw new Error(`align-timeline-translation only supports timeline files with extension 'json'`);
|
|
727
|
+
}
|
|
728
|
+
if (!existsSync(timelineFilename)) {
|
|
729
|
+
throw new Error(`The given timeline file '${timelineFilename}' was not found.`);
|
|
730
|
+
}
|
|
731
|
+
const timeline = await readAndParseJsonFile(timelineFilename);
|
|
732
|
+
const translationFilePath = operationArgs[1];
|
|
733
|
+
if (translationFilePath == undefined) {
|
|
734
|
+
throw new Error(`align-timeline-translation requires a second argument containing the translated reference file path.`);
|
|
735
|
+
}
|
|
736
|
+
if (!existsSync(translationFilePath)) {
|
|
737
|
+
throw new Error(`The given reference file '${translationFilePath}' was not found.`);
|
|
738
|
+
}
|
|
739
|
+
const translationFileExtension = getLowercaseFileExtension(translationFilePath);
|
|
740
|
+
const translationFileContent = await readFile(translationFilePath, { encoding: 'utf-8' });
|
|
741
|
+
let translationText;
|
|
742
|
+
if (translationFileExtension == 'txt') {
|
|
743
|
+
translationText = translationFileContent;
|
|
744
|
+
}
|
|
745
|
+
else if (translationFileExtension == 'html' || translationFileExtension == 'htm') {
|
|
746
|
+
translationText = await convertHtmlToText(translationFileContent);
|
|
747
|
+
}
|
|
748
|
+
else if (translationFileExtension == 'srt' || translationFileExtension == 'vtt') {
|
|
749
|
+
translationText = subtitlesToText(translationFileContent);
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
throw new Error(`align only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`);
|
|
753
|
+
}
|
|
754
|
+
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TimelineTranslationAlignmentOptions');
|
|
755
|
+
const { timeline: translationTimeline, wordTimeline: translationWordTimeline, rawAudio } = await API.alignTimelineTranslation(timeline, translationText, options);
|
|
756
|
+
if (outputFilenames.length > 0) {
|
|
757
|
+
logger.start('\nWrite output files');
|
|
758
|
+
}
|
|
759
|
+
const allowOverwrite = getWithDefault(cliOptions.overwrite, overwriteByDefault);
|
|
760
|
+
for (const outputFilename of outputFilenames) {
|
|
761
|
+
const partPatternMatch = outputFilename.match(filenamePlaceholderPattern);
|
|
762
|
+
if (partPatternMatch) {
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
const fileSaver = getFileSaver(outputFilename, allowOverwrite);
|
|
766
|
+
await fileSaver(getEmptyRawAudio(1, 16000), translationTimeline, translationText, options.subtitles);
|
|
767
|
+
}
|
|
768
|
+
logger.end();
|
|
769
|
+
if (cliOptions.play && rawAudio) {
|
|
770
|
+
const normalizedAudioToPlay = normalizeAudioLevel(rawAudio);
|
|
771
|
+
let transcriptToPlay;
|
|
772
|
+
let timelineToPlay;
|
|
773
|
+
transcriptToPlay = translationText;
|
|
774
|
+
timelineToPlay = translationWordTimeline;
|
|
775
|
+
await playAudioWithWordTimeline(normalizedAudioToPlay, timelineToPlay, transcriptToPlay);
|
|
605
776
|
}
|
|
606
777
|
}
|
|
607
|
-
async function translateSpeech(operationData) {
|
|
778
|
+
export async function translateSpeech(operationData) {
|
|
608
779
|
const logger = new Logger();
|
|
609
780
|
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
610
781
|
const inputFilename = operationArgs[0];
|
|
@@ -667,7 +838,7 @@ async function translateSpeech(operationData) {
|
|
|
667
838
|
await playAudioWithWordTimeline(normalizedAudioToPlay, timelineToPlay, transcriptToPlay);
|
|
668
839
|
}
|
|
669
840
|
}
|
|
670
|
-
async function detectLanguage(operationData, mode) {
|
|
841
|
+
export async function detectLanguage(operationData, mode) {
|
|
671
842
|
const logger = new Logger();
|
|
672
843
|
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
673
844
|
const inputFilePath = operationArgs[0];
|
|
@@ -721,7 +892,7 @@ async function detectLanguage(operationData, mode) {
|
|
|
721
892
|
}
|
|
722
893
|
logger.end();
|
|
723
894
|
}
|
|
724
|
-
async function detectVoiceActivity(operationData) {
|
|
895
|
+
export async function detectVoiceActivity(operationData) {
|
|
725
896
|
const logger = new Logger();
|
|
726
897
|
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
727
898
|
const audioFilename = operationArgs[0];
|
|
@@ -766,7 +937,7 @@ async function detectVoiceActivity(operationData) {
|
|
|
766
937
|
await playAudioWithWordTimeline(normalizedAudio, timelineToPlay);
|
|
767
938
|
}
|
|
768
939
|
}
|
|
769
|
-
async function denoise(operationData) {
|
|
940
|
+
export async function denoise(operationData) {
|
|
770
941
|
const logger = new Logger();
|
|
771
942
|
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
772
943
|
const audioFilename = operationArgs[0];
|
|
@@ -796,7 +967,7 @@ async function denoise(operationData) {
|
|
|
796
967
|
await playAudioSamples(denoisedAudio);
|
|
797
968
|
}
|
|
798
969
|
}
|
|
799
|
-
async function isolate(operationData) {
|
|
970
|
+
export async function isolate(operationData) {
|
|
800
971
|
const logger = new Logger();
|
|
801
972
|
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
802
973
|
const audioFilename = operationArgs[0];
|
|
@@ -825,7 +996,7 @@ async function isolate(operationData) {
|
|
|
825
996
|
await playAudioSamples(isolatedRawAudio);
|
|
826
997
|
}
|
|
827
998
|
}
|
|
828
|
-
async function listEngines(operationData) {
|
|
999
|
+
export async function listEngines(operationData) {
|
|
829
1000
|
const logger = new Logger();
|
|
830
1001
|
const { operationArgs } = operationData;
|
|
831
1002
|
const targetOperation = operationArgs[0];
|
|
@@ -905,7 +1076,7 @@ async function listEngines(operationData) {
|
|
|
905
1076
|
}
|
|
906
1077
|
}
|
|
907
1078
|
}
|
|
908
|
-
async function listTTSVoices(operationData) {
|
|
1079
|
+
export async function listTTSVoices(operationData) {
|
|
909
1080
|
const logger = new Logger();
|
|
910
1081
|
const { operationArgs, operationOptionsLookup, cliOptions } = operationData;
|
|
911
1082
|
const targetEngine = operationArgs[0];
|
|
@@ -947,7 +1118,7 @@ async function listTTSVoices(operationData) {
|
|
|
947
1118
|
}
|
|
948
1119
|
logger.end();
|
|
949
1120
|
}
|
|
950
|
-
async function installPackages(operationData) {
|
|
1121
|
+
export async function installPackages(operationData) {
|
|
951
1122
|
const logger = new Logger();
|
|
952
1123
|
const { operationArgs } = operationData;
|
|
953
1124
|
if (operationArgs.length == 0) {
|
|
@@ -973,7 +1144,7 @@ async function installPackages(operationData) {
|
|
|
973
1144
|
}
|
|
974
1145
|
}
|
|
975
1146
|
}
|
|
976
|
-
async function uninstallPackages(operationData) {
|
|
1147
|
+
export async function uninstallPackages(operationData) {
|
|
977
1148
|
const logger = new Logger();
|
|
978
1149
|
const { operationArgs } = operationData;
|
|
979
1150
|
if (operationArgs.length == 0) {
|
|
@@ -994,7 +1165,7 @@ async function uninstallPackages(operationData) {
|
|
|
994
1165
|
logger.log(`The packages ${failedPackageNames.join(', ')} failed to uninstall`, 'error');
|
|
995
1166
|
}
|
|
996
1167
|
}
|
|
997
|
-
async function listPackages(operationData) {
|
|
1168
|
+
export async function listPackages(operationData) {
|
|
998
1169
|
const logger = new Logger();
|
|
999
1170
|
const packagesDir = await ensureAndGetPackagesDir();
|
|
1000
1171
|
const installedPackageNames = await readdir(packagesDir);
|
|
@@ -1017,7 +1188,7 @@ async function listPackages(operationData) {
|
|
|
1017
1188
|
logger.log(``);
|
|
1018
1189
|
logger.log(installedPackageNamesFormatted.join('\n'));
|
|
1019
1190
|
}
|
|
1020
|
-
async function serve(operationData) {
|
|
1191
|
+
export async function serve(operationData) {
|
|
1021
1192
|
const { operationOptionsLookup } = operationData;
|
|
1022
1193
|
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'ServerOptions');
|
|
1023
1194
|
async function onServerStarted(serverOptions) {
|
|
@@ -1143,13 +1314,13 @@ async function optionsLookupToTypedObject(cliOptionsMap, optionsRoot, additional
|
|
|
1143
1314
|
return resultingObj;
|
|
1144
1315
|
}
|
|
1145
1316
|
let cachedOptionsSchema;
|
|
1146
|
-
|
|
1317
|
+
async function getOptionsSchema() {
|
|
1147
1318
|
if (!cachedOptionsSchema) {
|
|
1148
1319
|
cachedOptionsSchema = await readAndParseJsonFile(resolveToModuleRootDir('data/schemas/options.json'));
|
|
1149
1320
|
}
|
|
1150
1321
|
return cachedOptionsSchema;
|
|
1151
1322
|
}
|
|
1152
|
-
|
|
1323
|
+
async function checkOutputFilenames(outputFilenames, acceptMediaOutputs, acceptMetadataOutputs, acceptSubtitleOutputs) {
|
|
1153
1324
|
const supportedFileExtensions = [];
|
|
1154
1325
|
if (acceptMediaOutputs) {
|
|
1155
1326
|
supportedFileExtensions.push(...supportedOutputMediaFileExtensions);
|
|
@@ -1188,7 +1359,7 @@ export async function checkOutputFilenames(outputFilenames, acceptMediaOutputs,
|
|
|
1188
1359
|
}
|
|
1189
1360
|
return { includesPlaceholderPattern };
|
|
1190
1361
|
}
|
|
1191
|
-
|
|
1362
|
+
async function writeOutputFilesForSegment(outputFilenames, index, total, audio, timeline, text, language, allowOverwrite) {
|
|
1192
1363
|
const digitCount = Math.max((total + 1).toString().length, 2);
|
|
1193
1364
|
const segmentWords = (await splitToWords(text, language)).filter(text => wordCharacterPattern.test(text));
|
|
1194
1365
|
const segmentJoinedWords = segmentWords.join(' ').trim();
|