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.
- package/data/schemas/options.json +310 -25
- package/dist/alignment/DTWMfccSequenceAlignment.d.ts +1 -1
- package/dist/alignment/DTWMfccSequenceAlignment.js +5 -5
- package/dist/alignment/DTWSequenceAlignmentWindowed.js +1 -3
- package/dist/alignment/DTWSequenceAlignmentWindowed.js.map +1 -1
- package/dist/alignment/{TextAlignment.d.ts → SemanticTextAlignment.d.ts} +4 -2
- package/dist/alignment/SemanticTextAlignment.js +336 -0
- package/dist/alignment/SemanticTextAlignment.js.map +1 -0
- package/dist/alignment/SpeechAlignment.d.ts +4 -3
- package/dist/alignment/SpeechAlignment.js +130 -39
- package/dist/alignment/SpeechAlignment.js.map +1 -1
- package/dist/api/API.d.ts +7 -3
- 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 +13 -5
- 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.d.ts +0 -1
- 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 +25 -0
- package/dist/api/TextTranslation.js +101 -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/audio/AudioBufferConversion.d.ts +0 -1
- package/dist/audio/AudioPlayer.d.ts +0 -1
- package/dist/audio/AudioPlayer.js +62 -41
- package/dist/audio/AudioPlayer.js.map +1 -1
- package/dist/audio/AudioUtilities.d.ts +0 -1
- package/dist/cli/CLI.d.ts +28 -7
- package/dist/cli/CLI.js +265 -37
- package/dist/cli/CLI.js.map +1 -1
- package/dist/codecs/FFMpegTranscoder.d.ts +0 -1
- package/dist/codecs/FFMpegTranscoder.js +7 -0
- package/dist/codecs/FFMpegTranscoder.js.map +1 -1
- package/dist/codecs/TIMITCodec.d.ts +0 -1
- package/dist/codecs/WaveCodec.d.ts +0 -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 +9 -9
- package/dist/math/VectorMath.js +10 -10
- package/dist/math/VectorMath.js.map +1 -1
- package/dist/nlp/ChineseSegmentation.js +4 -4
- package/dist/nlp/ChineseSegmentation.js.map +1 -1
- package/dist/nlp/Segmentation.d.ts +2 -2
- package/dist/nlp/Segmentation.js +20 -13
- package/dist/nlp/Segmentation.js.map +1 -1
- package/dist/recognition/OpenAICloudSTT.d.ts +2 -1
- package/dist/recognition/OpenAICloudSTT.js +30 -19
- package/dist/recognition/OpenAICloudSTT.js.map +1 -1
- package/dist/recognition/SileroSTT.d.ts +0 -1
- package/dist/recognition/WhisperCppSTT.d.ts +3 -3
- package/dist/recognition/WhisperCppSTT.js +21 -9
- package/dist/recognition/WhisperCppSTT.js.map +1 -1
- package/dist/recognition/WhisperSTT.d.ts +9 -6
- package/dist/recognition/WhisperSTT.js +227 -46
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/server/Client.d.ts +3 -4
- package/dist/server/Client.js.map +1 -1
- package/dist/server/Worker.d.ts +3 -3
- package/dist/server/Worker.js +3 -2
- package/dist/server/Worker.js.map +1 -1
- package/dist/source-separation/MDXNetSourceSeparation.d.ts +0 -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 +12 -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 -2
- package/dist/speech-language-detection/SileroLanguageDetection.js +1 -1
- package/dist/speech-language-detection/SileroLanguageDetection.js.map +1 -1
- package/dist/subtitles/Subtitles.js +2 -2
- package/dist/subtitles/Subtitles.js.map +1 -1
- package/dist/synthesis/GoogleCloudTTS.d.ts +0 -1
- package/dist/synthesis/GoogleTranslateTTS.d.ts +0 -1
- package/dist/synthesis/GoogleTranslateTTS.js +6 -21
- package/dist/synthesis/GoogleTranslateTTS.js.map +1 -1
- package/dist/synthesis/StreamlabsPollyTTS.d.ts +0 -1
- package/dist/synthesis/VitsTTS.d.ts +0 -1
- package/dist/synthesis/VitsTTS.js +30 -0
- package/dist/synthesis/VitsTTS.js.map +1 -1
- package/dist/tests/Test.js +0 -31
- 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/DeepLTextTranslation.d.ts +2 -0
- package/dist/text-translation/DeepLTextTranslation.js +67 -0
- package/dist/text-translation/DeepLTextTranslation.js.map +1 -0
- package/dist/text-translation/GoogleTranslateTextTranslation.d.ts +10 -0
- package/dist/text-translation/GoogleTranslateTextTranslation.js +554 -0
- package/dist/text-translation/GoogleTranslateTextTranslation.js.map +1 -0
- package/dist/text-translation/NLLBTextTranslation.d.ts +2 -1
- package/dist/text-translation/NLLBTextTranslation.js +249 -19
- package/dist/text-translation/NLLBTextTranslation.js.map +1 -1
- package/dist/utilities/BinaryArrayConversion.d.ts +0 -1
- package/dist/utilities/BrowserRequestHeaders.d.ts +6 -0
- package/dist/utilities/BrowserRequestHeaders.js +52 -0
- package/dist/utilities/BrowserRequestHeaders.js.map +1 -0
- package/dist/utilities/BufferFileReadStream.d.ts +20 -0
- package/dist/utilities/BufferFileReadStream.js +81 -0
- package/dist/utilities/BufferFileReadStream.js.map +1 -0
- package/dist/utilities/DynamicUint8Array.d.ts +9 -0
- package/dist/utilities/DynamicUint8Array.js +31 -0
- package/dist/utilities/DynamicUint8Array.js.map +1 -0
- package/dist/utilities/FileSystem.d.ts +0 -2
- package/dist/utilities/Hashing.d.ts +3 -10
- package/dist/utilities/Hashing.js +10 -127
- package/dist/utilities/Hashing.js.map +1 -1
- package/dist/utilities/LEB128.d.ts +15 -5
- package/dist/utilities/LEB128.js +199 -119
- package/dist/utilities/LEB128.js.map +1 -1
- package/dist/utilities/LPVarInt.d.ts +11 -0
- package/dist/utilities/LPVarInt.js +187 -0
- package/dist/utilities/LPVarInt.js.map +1 -0
- package/dist/utilities/Locale.d.ts +1 -1
- package/dist/utilities/Locale.js +1 -1
- package/dist/utilities/OnnxUtilities.d.ts +1 -2
- package/dist/utilities/PVarInt.d.ts +4 -0
- package/dist/utilities/PVarInt.js +166 -0
- package/dist/utilities/PVarInt.js.map +1 -0
- package/dist/utilities/PackageManager.js +48 -25
- package/dist/utilities/PackageManager.js.map +1 -1
- package/dist/utilities/RandomGenerator.d.ts +3 -17
- package/dist/utilities/RandomGenerator.js +12 -81
- package/dist/utilities/RandomGenerator.js.map +1 -1
- package/dist/utilities/Timeline.d.ts +2 -0
- package/dist/utilities/Timeline.js +129 -20
- package/dist/utilities/Timeline.js.map +1 -1
- package/dist/utilities/Utilities.d.ts +1 -3
- package/dist/utilities/Utilities.js +30 -3
- package/dist/utilities/Utilities.js.map +1 -1
- package/dist/utilities/VarInt.d.ts +4 -0
- package/dist/utilities/VarInt.js +166 -0
- package/dist/utilities/VarInt.js.map +1 -0
- package/dist/utilities/VirtualFileReadStream.d.ts +20 -0
- package/dist/utilities/VirtualFileReadStream.js +79 -0
- package/dist/utilities/VirtualFileReadStream.js.map +1 -0
- package/dist/utilities/WebReader.js +7 -23
- package/dist/utilities/WebReader.js.map +1 -1
- package/dist/voice-activity-detection/SileroVAD.d.ts +0 -1
- package/docs/API.md +105 -3
- package/docs/CLI.md +51 -1
- package/docs/Engines.md +32 -3
- package/docs/Options.md +53 -12
- package/docs/Tasklist.md +1 -13
- package/package.json +20 -24
- package/src/alignment/DTWMfccSequenceAlignment.ts +5 -5
- package/src/alignment/DTWSequenceAlignmentWindowed.ts +1 -3
- package/src/alignment/SemanticTextAlignment.ts +467 -0
- package/src/alignment/SpeechAlignment.ts +214 -56
- package/src/api/API.ts +18 -2
- package/src/api/APIOptions.ts +14 -1
- package/src/api/Alignment.ts +31 -9
- 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 +177 -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/audio/AudioPlayer.ts +2 -0
- package/src/cli/CLI.ts +376 -40
- 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 +15 -15
- package/src/nlp/ChineseSegmentation.ts +6 -4
- package/src/nlp/Segmentation.ts +18 -13
- package/src/recognition/OpenAICloudSTT.ts +47 -29
- package/src/recognition/WhisperCppSTT.ts +26 -11
- package/src/recognition/WhisperSTT.ts +364 -49
- 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/subtitles/Subtitles.ts +2 -2
- package/src/synthesis/GoogleTranslateTTS.ts +7 -21
- package/src/synthesis/VitsTTS.ts +31 -3
- package/src/tests/Test.ts +1 -38
- package/src/text-language-detection/FastTextLanguageDetection.ts +1 -1
- package/src/text-language-detection/TinyLDLanguageDetection.ts +1 -1
- package/src/text-translation/DeepLTextTranslation.ts +88 -0
- package/src/text-translation/GoogleTranslateTextTranslation.ts +667 -0
- package/src/text-translation/NLLBTextTranslation.ts +261 -21
- package/src/typings/Fillers.d.ts +25 -2
- package/src/utilities/BrowserRequestHeaders.ts +59 -0
- package/src/utilities/DynamicUint8Array.ts +39 -0
- package/src/utilities/Hashing.ts +14 -167
- package/src/utilities/LEB128.ts +273 -148
- package/src/utilities/LPVarInt.ts +292 -0
- package/src/utilities/Locale.ts +1 -1
- package/src/utilities/OnnxUtilities.ts +1 -1
- package/src/utilities/PackageManager.ts +51 -30
- package/src/utilities/RandomGenerator.ts +12 -113
- package/src/utilities/Timeline.ts +162 -23
- package/src/utilities/Utilities.ts +40 -3
- package/src/utilities/VirtualFileReadStream.ts +109 -0
- package/src/utilities/WebReader.ts +9 -23
- package/dist/alignment/TextAlignment.js +0 -156
- 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 -234
- /package/dist/api/{Translation.d.ts → SpeechTranslation.d.ts} +0 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { Logger } from '../utilities/Logger.js';
|
|
2
|
+
import { loadPackage } from '../utilities/PackageManager.js';
|
|
3
|
+
import { alignDTWWindowed } from './DTWSequenceAlignmentWindowed.js';
|
|
4
|
+
import { cosineDistance } from '../math/VectorMath.js';
|
|
5
|
+
import { isPunctuation, isWord, splitToSentences, splitToWords } from '../nlp/Segmentation.js';
|
|
6
|
+
import { extractEntries } from '../utilities/Timeline.js';
|
|
7
|
+
export async function alignTimelineToTextSemantically(timeline, text, textLangCode) {
|
|
8
|
+
const logger = new Logger();
|
|
9
|
+
logger.start(`Prepare text for semantic alignment`);
|
|
10
|
+
const timelineSentenceEntries = extractEntries(timeline, entry => entry.type === 'sentence');
|
|
11
|
+
const timelineWordEntryGroups = [];
|
|
12
|
+
const timelineWordGroups = [];
|
|
13
|
+
for (const sentenceEntry of timelineSentenceEntries) {
|
|
14
|
+
const wordEntryGroup = sentenceEntry.timeline
|
|
15
|
+
.filter(wordEntry => isWord(wordEntry.text));
|
|
16
|
+
timelineWordEntryGroups.push(wordEntryGroup);
|
|
17
|
+
timelineWordGroups.push(wordEntryGroup.map(wordEntry => wordEntry.text));
|
|
18
|
+
}
|
|
19
|
+
const timelineWordEntriesFiltered = timelineWordEntryGroups.flat();
|
|
20
|
+
const textSentences = splitToSentences(text, textLangCode);
|
|
21
|
+
const textWordGroups = [];
|
|
22
|
+
for (const sentenceText of textSentences) {
|
|
23
|
+
let wordGroup = await splitToWords(sentenceText, textLangCode);
|
|
24
|
+
wordGroup = wordGroup.filter(word => isWord(word));
|
|
25
|
+
textWordGroups.push(wordGroup);
|
|
26
|
+
}
|
|
27
|
+
const textWords = textWordGroups.flat();
|
|
28
|
+
logger.end();
|
|
29
|
+
const wordMappingEntries = await alignWordsToWordsSemantically(timelineWordGroups, textWordGroups);
|
|
30
|
+
logger.start(`Build timeline for translation`);
|
|
31
|
+
const mappingGroups = new Map();
|
|
32
|
+
for (const wordMappingEntry of wordMappingEntries) {
|
|
33
|
+
const wordIndex1 = wordMappingEntry.wordIndex1;
|
|
34
|
+
const wordIndex2 = wordMappingEntry.wordIndex2;
|
|
35
|
+
let group = mappingGroups.get(wordIndex1);
|
|
36
|
+
if (!group) {
|
|
37
|
+
group = [];
|
|
38
|
+
mappingGroups.set(wordIndex1, group);
|
|
39
|
+
}
|
|
40
|
+
if (!group.includes(wordIndex2)) {
|
|
41
|
+
group.push(wordIndex2);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const timeSlicesLookup = new Map();
|
|
45
|
+
for (const [wordIndex1, mappedWordIndexes] of mappingGroups) {
|
|
46
|
+
if (mappedWordIndexes.length === 0) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const startTime = timelineWordEntriesFiltered[wordIndex1].startTime;
|
|
50
|
+
const endTime = timelineWordEntriesFiltered[wordIndex1].endTime;
|
|
51
|
+
const splitCount = mappedWordIndexes.length;
|
|
52
|
+
const sliceDuration = (endTime - startTime) / splitCount;
|
|
53
|
+
let timeOffset = 0;
|
|
54
|
+
for (let i = 0; i < splitCount; i++) {
|
|
55
|
+
const timeSlice = {
|
|
56
|
+
startTime: startTime + timeOffset,
|
|
57
|
+
endTime: startTime + timeOffset + sliceDuration
|
|
58
|
+
};
|
|
59
|
+
const wordIndex2 = mappedWordIndexes[i];
|
|
60
|
+
let timeSlicesForTargetWord = timeSlicesLookup.get(wordIndex2);
|
|
61
|
+
if (!timeSlicesForTargetWord) {
|
|
62
|
+
timeSlicesForTargetWord = [];
|
|
63
|
+
timeSlicesLookup.set(wordIndex2, timeSlicesForTargetWord);
|
|
64
|
+
}
|
|
65
|
+
timeSlicesForTargetWord.push(timeSlice);
|
|
66
|
+
timeOffset += sliceDuration;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const resultTimeline = [];
|
|
70
|
+
for (const [key, value] of timeSlicesLookup) {
|
|
71
|
+
resultTimeline.push({
|
|
72
|
+
type: 'word',
|
|
73
|
+
text: textWords[key],
|
|
74
|
+
startTime: value[0].startTime,
|
|
75
|
+
endTime: value[value.length - 1].endTime
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
logger.end();
|
|
79
|
+
return resultTimeline;
|
|
80
|
+
}
|
|
81
|
+
export async function alignWordsToWordsSemantically(wordsGroups1, wordsGroups2, windowTokenCount = 20000) {
|
|
82
|
+
const logger = new Logger();
|
|
83
|
+
// Load embedding model
|
|
84
|
+
const modelPath = await loadPackage(`xenova-multilingual-e5-small-fp16`);
|
|
85
|
+
const embeddingModel = new E5TextEmbedding(modelPath);
|
|
86
|
+
logger.start(`Initialize E5 embedding model`);
|
|
87
|
+
await embeddingModel.initializeIfNeeded();
|
|
88
|
+
async function extractEmbeddingsFromWordGroups(wordGroups) {
|
|
89
|
+
const logger = new Logger();
|
|
90
|
+
const maxTokensPerFragment = 512;
|
|
91
|
+
const { Tensor } = await import('@echogarden/transformers-nodejs-lite');
|
|
92
|
+
const words = [];
|
|
93
|
+
const embeddings = [];
|
|
94
|
+
const tokenToWordIndexMapping = [];
|
|
95
|
+
for (const wordGroup of wordGroups) {
|
|
96
|
+
const { joinedText: joinedTextForGroup, offsets: offsetsForGroup } = joinAndGetOffsets(wordGroup);
|
|
97
|
+
logger.start(`Tokenize text`);
|
|
98
|
+
const inputsForGroup = await embeddingModel.tokenizeToModelInputs(joinedTextForGroup);
|
|
99
|
+
logger.start(`Infer embeddings for text`);
|
|
100
|
+
const allTokenIds = inputsForGroup['input_ids'].data;
|
|
101
|
+
const allAttentionMask = inputsForGroup['attention_mask'].data;
|
|
102
|
+
let embeddingsForGroup = [];
|
|
103
|
+
for (let tokenStart = 0; tokenStart < allTokenIds.length; tokenStart += maxTokensPerFragment) {
|
|
104
|
+
const tokenEnd = Math.min(tokenStart + maxTokensPerFragment, allTokenIds.length);
|
|
105
|
+
const fragmentTokenCount = tokenEnd - tokenStart;
|
|
106
|
+
const fragmentInputIdsTensor = new Tensor('int64', allTokenIds.slice(tokenStart, tokenEnd), [1, fragmentTokenCount]);
|
|
107
|
+
const fragmentAttentionMaskTensor = new Tensor('int64', allAttentionMask.slice(tokenStart, tokenEnd), [1, fragmentTokenCount]);
|
|
108
|
+
const inputsForFragment = { input_ids: fragmentInputIdsTensor, attention_mask: fragmentAttentionMaskTensor };
|
|
109
|
+
const embeddingsForFragment = await embeddingModel.inferTokenEmbeddings(inputsForFragment);
|
|
110
|
+
embeddingsForGroup.push(...embeddingsForFragment);
|
|
111
|
+
}
|
|
112
|
+
logger.start(`Compute token to word mapping for text`);
|
|
113
|
+
const filteredEmbeddingsForGroup = embeddingsForGroup.filter((embedding) => embedding.text !== '▁' && embedding.text !== '<s>' && embedding.text !== '</s>');
|
|
114
|
+
const tokenToWordIndexMappingForGroup = mapTokenEmbeddingsToWordIndexes(filteredEmbeddingsForGroup, joinedTextForGroup, offsetsForGroup);
|
|
115
|
+
const tokenToWordIndexMappingForGroupWithOffset = tokenToWordIndexMappingForGroup.map(value => words.length + value);
|
|
116
|
+
embeddings.push(...filteredEmbeddingsForGroup);
|
|
117
|
+
tokenToWordIndexMapping.push(...tokenToWordIndexMappingForGroupWithOffset);
|
|
118
|
+
words.push(...wordGroup);
|
|
119
|
+
}
|
|
120
|
+
return { words, embeddings, tokenToWordIndexMapping };
|
|
121
|
+
}
|
|
122
|
+
logger.start(`Extract embeddings from source 1`);
|
|
123
|
+
const { words: words1, embeddings: embeddings1, tokenToWordIndexMapping: tokenToWordIndexMapping1 } = await extractEmbeddingsFromWordGroups(wordsGroups1);
|
|
124
|
+
logger.start(`Extract embeddings from source 2`);
|
|
125
|
+
const { words: words2, embeddings: embeddings2, tokenToWordIndexMapping: tokenToWordIndexMapping2 } = await extractEmbeddingsFromWordGroups(wordsGroups2);
|
|
126
|
+
// Align
|
|
127
|
+
function costFunction(a, b) {
|
|
128
|
+
const aIsPunctuation = isPunctuation(a.text);
|
|
129
|
+
const bIsPunctuation = isPunctuation(b.text);
|
|
130
|
+
if (aIsPunctuation === bIsPunctuation) {
|
|
131
|
+
return cosineDistance(a.embeddingVector, b.embeddingVector);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
return 1.0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
logger.start(`Align token embedding vectors using DTW`);
|
|
138
|
+
const { path } = alignDTWWindowed(embeddings1, embeddings2, costFunction, windowTokenCount);
|
|
139
|
+
// Use alignment path to words to words
|
|
140
|
+
logger.start(`Map tokens to words`);
|
|
141
|
+
const wordMapping = [];
|
|
142
|
+
for (let i = 0; i < path.length; i++) {
|
|
143
|
+
const pathEntry = path[i];
|
|
144
|
+
const sourceTokenIndex = pathEntry.source;
|
|
145
|
+
const destTokenIndex = pathEntry.dest;
|
|
146
|
+
const mappedWordIndex1 = tokenToWordIndexMapping1[sourceTokenIndex];
|
|
147
|
+
const mappedWordIndex2 = tokenToWordIndexMapping2[destTokenIndex];
|
|
148
|
+
wordMapping.push({
|
|
149
|
+
wordIndex1: mappedWordIndex1,
|
|
150
|
+
word1: words1[mappedWordIndex1],
|
|
151
|
+
wordIndex2: mappedWordIndex2,
|
|
152
|
+
word2: words2[mappedWordIndex2],
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
logger.end();
|
|
156
|
+
return wordMapping;
|
|
157
|
+
}
|
|
158
|
+
function mapTokenEmbeddingsToWordIndexes(embeddings, text, textWordOffsets) {
|
|
159
|
+
const tokenToWordIndex = [];
|
|
160
|
+
let currentTextOffset = 0;
|
|
161
|
+
for (let i = 0; i < embeddings.length; i++) {
|
|
162
|
+
const embedding = embeddings[i];
|
|
163
|
+
let tokenText = embedding.text;
|
|
164
|
+
if (tokenText === '<s>' || tokenText === '</s>') {
|
|
165
|
+
tokenToWordIndex.push(-1);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (tokenText.startsWith('▁')) {
|
|
169
|
+
tokenText = tokenText.substring(1);
|
|
170
|
+
}
|
|
171
|
+
const matchPosition = text.indexOf(tokenText, currentTextOffset);
|
|
172
|
+
if (matchPosition === -1) {
|
|
173
|
+
throw new Error(`Token '${tokenText}' not found in text`);
|
|
174
|
+
}
|
|
175
|
+
currentTextOffset = matchPosition + tokenText.length;
|
|
176
|
+
let tokenMatchingWordIndex = textWordOffsets.findIndex((index) => index > matchPosition);
|
|
177
|
+
if (tokenMatchingWordIndex === -1) {
|
|
178
|
+
throw new Error(`Token '${tokenText}' not found in text`);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
tokenMatchingWordIndex = Math.max(tokenMatchingWordIndex - 1, 0);
|
|
182
|
+
}
|
|
183
|
+
tokenToWordIndex.push(tokenMatchingWordIndex);
|
|
184
|
+
}
|
|
185
|
+
return tokenToWordIndex;
|
|
186
|
+
}
|
|
187
|
+
function joinAndGetOffsets(words) {
|
|
188
|
+
let joinedText = '';
|
|
189
|
+
const offsets = [];
|
|
190
|
+
let offset = 0;
|
|
191
|
+
for (const word of words) {
|
|
192
|
+
const extendedWord = `${word} `;
|
|
193
|
+
joinedText += extendedWord;
|
|
194
|
+
offsets.push(offset);
|
|
195
|
+
offset += extendedWord.length;
|
|
196
|
+
}
|
|
197
|
+
offsets.push(joinedText.length);
|
|
198
|
+
return { joinedText, offsets };
|
|
199
|
+
}
|
|
200
|
+
export class E5TextEmbedding {
|
|
201
|
+
modelPath;
|
|
202
|
+
tokenizer;
|
|
203
|
+
model;
|
|
204
|
+
constructor(modelPath) {
|
|
205
|
+
this.modelPath = modelPath;
|
|
206
|
+
}
|
|
207
|
+
async tokenizeToModelInputs(text) {
|
|
208
|
+
await this.initializeIfNeeded();
|
|
209
|
+
const inputs = await this.tokenizer(text);
|
|
210
|
+
return inputs;
|
|
211
|
+
}
|
|
212
|
+
async inferTokenEmbeddings(inputs) {
|
|
213
|
+
await this.initializeIfNeeded();
|
|
214
|
+
const tokensText = this.tokenizer.model.convert_ids_to_tokens(Array.from(inputs.input_ids.data));
|
|
215
|
+
const result = await this.model(inputs);
|
|
216
|
+
const lastHiddenState = result.last_hidden_state;
|
|
217
|
+
const tokenCount = lastHiddenState.dims[1];
|
|
218
|
+
const embeddingSize = lastHiddenState.dims[2];
|
|
219
|
+
const tokenEmbeddings = [];
|
|
220
|
+
for (let i = 0; i < tokenCount; i++) {
|
|
221
|
+
const tokenEmbeddingVector = lastHiddenState.data.slice(i * embeddingSize, (i + 1) * embeddingSize);
|
|
222
|
+
const tokenId = Number(inputs.input_ids.data[i]);
|
|
223
|
+
const tokenText = tokensText[i];
|
|
224
|
+
tokenEmbeddings.push({
|
|
225
|
+
id: tokenId,
|
|
226
|
+
text: tokenText,
|
|
227
|
+
embeddingVector: tokenEmbeddingVector
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return tokenEmbeddings;
|
|
231
|
+
}
|
|
232
|
+
async initializeIfNeeded() {
|
|
233
|
+
if (this.tokenizer && this.model) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const { AutoTokenizer, AutoModel } = await import('@echogarden/transformers-nodejs-lite');
|
|
237
|
+
this.tokenizer = await AutoTokenizer.from_pretrained(this.modelPath);
|
|
238
|
+
this.model = await AutoModel.from_pretrained(this.modelPath);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
export const e5SupportedLanguages = [
|
|
242
|
+
'af', // Afrikaans
|
|
243
|
+
'am', // Amharic
|
|
244
|
+
'ar', // Arabic
|
|
245
|
+
'as', // Assamese
|
|
246
|
+
'az', // Azerbaijani
|
|
247
|
+
'be', // Belarusian
|
|
248
|
+
'bg', // Bulgarian
|
|
249
|
+
'bn', // Bengali
|
|
250
|
+
'br', // Breton
|
|
251
|
+
'bs', // Bosnian
|
|
252
|
+
'ca', // Catalan
|
|
253
|
+
'cs', // Czech
|
|
254
|
+
'cy', // Welsh
|
|
255
|
+
'da', // Danish
|
|
256
|
+
'de', // German
|
|
257
|
+
'el', // Greek
|
|
258
|
+
'en', // English
|
|
259
|
+
'eo', // Esperanto
|
|
260
|
+
'es', // Spanish
|
|
261
|
+
'et', // Estonian
|
|
262
|
+
'eu', // Basque
|
|
263
|
+
'fa', // Persian
|
|
264
|
+
'fi', // Finnish
|
|
265
|
+
'fr', // French
|
|
266
|
+
'fy', // Western Frisian
|
|
267
|
+
'ga', // Irish
|
|
268
|
+
'gd', // Scottish Gaelic
|
|
269
|
+
'gl', // Galician
|
|
270
|
+
'gu', // Gujarati
|
|
271
|
+
'ha', // Hausa
|
|
272
|
+
'he', // Hebrew
|
|
273
|
+
'hi', // Hindi
|
|
274
|
+
'hr', // Croatian
|
|
275
|
+
'hu', // Hungarian
|
|
276
|
+
'hy', // Armenian
|
|
277
|
+
'id', // Indonesian
|
|
278
|
+
'is', // Icelandic
|
|
279
|
+
'it', // Italian
|
|
280
|
+
'ja', // Japanese
|
|
281
|
+
'jv', // Javanese
|
|
282
|
+
'ka', // Georgian
|
|
283
|
+
'kk', // Kazakh
|
|
284
|
+
'km', // Khmer
|
|
285
|
+
'kn', // Kannada
|
|
286
|
+
'ko', // Korean
|
|
287
|
+
'ku', // Kurdish
|
|
288
|
+
'ky', // Kyrgyz
|
|
289
|
+
'la', // Latin
|
|
290
|
+
'lo', // Lao
|
|
291
|
+
'lt', // Lithuanian
|
|
292
|
+
'lv', // Latvian
|
|
293
|
+
'mg', // Malagasy
|
|
294
|
+
'mk', // Macedonian
|
|
295
|
+
'ml', // Malayalam
|
|
296
|
+
'mn', // Mongolian
|
|
297
|
+
'mr', // Marathi
|
|
298
|
+
'ms', // Malay
|
|
299
|
+
'my', // Burmese
|
|
300
|
+
'ne', // Nepali
|
|
301
|
+
'nl', // Dutch
|
|
302
|
+
'no', // Norwegian
|
|
303
|
+
'om', // Oromo
|
|
304
|
+
'or', // Oriya
|
|
305
|
+
'pa', // Panjabi
|
|
306
|
+
'pl', // Polish
|
|
307
|
+
'ps', // Pashto
|
|
308
|
+
'pt', // Portuguese
|
|
309
|
+
'ro', // Romanian
|
|
310
|
+
'ru', // Russian
|
|
311
|
+
'sa', // Sanskrit
|
|
312
|
+
'sd', // Sindhi
|
|
313
|
+
'si', // Sinhala
|
|
314
|
+
'sk', // Slovak
|
|
315
|
+
'sl', // Slovenian
|
|
316
|
+
'so', // Somali
|
|
317
|
+
'sq', // Albanian
|
|
318
|
+
'sr', // Serbian
|
|
319
|
+
'su', // Sundanese
|
|
320
|
+
'sv', // Swedish
|
|
321
|
+
'sw', // Swahili
|
|
322
|
+
'ta', // Tamil
|
|
323
|
+
'te', // Telugu
|
|
324
|
+
'th', // Thai
|
|
325
|
+
'tl', // Tagalog
|
|
326
|
+
'tr', // Turkish
|
|
327
|
+
'ug', // Uyghur
|
|
328
|
+
'uk', // Ukrainian
|
|
329
|
+
'ur', // Urdu
|
|
330
|
+
'uz', // Uzbek
|
|
331
|
+
'vi', // Vietnamese
|
|
332
|
+
'xh', // Xhosa
|
|
333
|
+
'yi', // Yiddish
|
|
334
|
+
'zh', // Chinese
|
|
335
|
+
];
|
|
336
|
+
//# sourceMappingURL=SemanticTextAlignment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SemanticTextAlignment.js","sourceRoot":"","sources":["../../src/alignment/SemanticTextAlignment.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import * as API from '../api/API.js';
|
|
2
2
|
import { Timeline, TimelineEntry } from '../utilities/Timeline.js';
|
|
3
3
|
import { RawAudio } from '../audio/AudioUtilities.js';
|
|
4
|
-
import { EspeakOptions } from '../synthesis/EspeakTTS.js';
|
|
4
|
+
import { EspeakEvent, EspeakOptions } from '../synthesis/EspeakTTS.js';
|
|
5
5
|
export declare function alignUsingDtw(sourceRawAudio: RawAudio, referenceRawAudio: RawAudio, referenceTimeline: Timeline, granularities: DtwGranularity[], windowDurations: number[]): Promise<TimelineEntry[]>;
|
|
6
6
|
export declare function alignUsingDtwWithRecognition(sourceRawAudio: RawAudio, referenceRawAudio: RawAudio, referenceTimeline: Timeline, recognitionTimeline: Timeline, granularities: DtwGranularity[], windowDurations: number[], espeakOptions: EspeakOptions, phoneAlignmentMethod?: API.PhoneAlignmentMethod): Promise<Timeline>;
|
|
7
|
+
export declare function alignUsingDtwWithEmbeddings(sourceRawAudio: RawAudio, referenceRawAudio: RawAudio, referenceTimeline: Timeline, language: string, granularities: DtwGranularity[], windowDurations: number[]): Promise<TimelineEntry[]>;
|
|
7
8
|
export declare function interpolatePhoneTimelines(sourceTimeline: Timeline, referenceTimeline: Timeline): Promise<Timeline>;
|
|
8
9
|
export declare function alignPhoneTimelines(sourceRawAudio: RawAudio, sourceWordTimeline: Timeline, referenceRawAudio: RawAudio, referenceTimeline: Timeline, windowDuration: number): Promise<Timeline>;
|
|
9
10
|
export declare function createAlignmentReferenceUsingEspeakForFragments(fragments: string[], espeakOptions: EspeakOptions): Promise<{
|
|
10
11
|
rawAudio: RawAudio;
|
|
11
12
|
timeline: Timeline;
|
|
12
|
-
events:
|
|
13
|
+
events: EspeakEvent[];
|
|
13
14
|
}>;
|
|
14
|
-
export declare function createAlignmentReferenceUsingEspeak(transcript: string, language: string, plaintextOptions?: API.PlainTextOptions, customLexiconPaths?: string[], insertSeparators?: boolean): Promise<{
|
|
15
|
+
export declare function createAlignmentReferenceUsingEspeak(transcript: string, language: string, plaintextOptions?: API.PlainTextOptions, customLexiconPaths?: string[], insertSeparators?: boolean, useKlatt?: boolean): Promise<{
|
|
15
16
|
referenceRawAudio: RawAudio;
|
|
16
17
|
referenceTimeline: TimelineEntry[];
|
|
17
18
|
espeakVoice: string;
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { clip } from '../utilities/Utilities.js';
|
|
1
|
+
import { clip, splitFloat32Array } from '../utilities/Utilities.js';
|
|
2
2
|
import { computeMFCCs, extendDefaultMfccOptions } from '../dsp/MFCC.js';
|
|
3
3
|
import { alignMFCC_DTW, getCostMatrixMemorySizeMB } from './DTWMfccSequenceAlignment.js';
|
|
4
4
|
import { Logger } from '../utilities/Logger.js';
|
|
5
|
-
import {
|
|
5
|
+
import { addTimeOffsetToTimeline } from '../utilities/Timeline.js';
|
|
6
|
+
import { concatAudioSegments, downmixToMonoAndNormalize, getEmptyRawAudio, getEndingSilentSampleCount, getRawAudioDuration, getStartingSilentSampleCount } from '../audio/AudioUtilities.js';
|
|
6
7
|
import chalk from 'chalk';
|
|
7
8
|
import { synthesize } from '../api/API.js';
|
|
8
9
|
import { resampleAudioSpeex } from '../dsp/SpeexResampler.js';
|
|
9
10
|
import { deepClone } from '../utilities/ObjectUtilities.js';
|
|
10
|
-
import { zeroIfNaN } from '../math/VectorMath.js';
|
|
11
|
+
import { cosineDistance, zeroIfNaN } from '../math/VectorMath.js';
|
|
12
|
+
import { alignDTWWindowed } from './DTWSequenceAlignmentWindowed.js';
|
|
13
|
+
import { loadPackage } from '../utilities/PackageManager.js';
|
|
14
|
+
import path from 'path';
|
|
11
15
|
export async function alignUsingDtw(sourceRawAudio, referenceRawAudio, referenceTimeline, granularities, windowDurations) {
|
|
12
16
|
const logger = new Logger();
|
|
13
17
|
if (windowDurations.length == 0) {
|
|
@@ -60,39 +64,7 @@ export async function alignUsingDtw(sourceRawAudio, referenceRawAudio, reference
|
|
|
60
64
|
logger.end();
|
|
61
65
|
}
|
|
62
66
|
logger.start('\nConvert path to timeline');
|
|
63
|
-
|
|
64
|
-
const referenceStartFrameIndex = Math.floor(timelineEntry.startTime * framesPerSecond);
|
|
65
|
-
const referenceEndFrameIndex = Math.floor(timelineEntry.endTime * framesPerSecond);
|
|
66
|
-
if (referenceStartFrameIndex < 0 || referenceEndFrameIndex < 0) {
|
|
67
|
-
throw new Error('Unexpected: encountered a negative timestamp in timeline');
|
|
68
|
-
}
|
|
69
|
-
const mappedStartFrameIndex = getMappedFrameIndexForPath(referenceStartFrameIndex, compactedPath, 'first');
|
|
70
|
-
const mappedEndFrameIndex = getMappedFrameIndexForPath(referenceEndFrameIndex, compactedPath, 'first');
|
|
71
|
-
let innerTimeline;
|
|
72
|
-
if (recurse && timelineEntry.timeline != null) {
|
|
73
|
-
innerTimeline = timelineEntry.timeline.map((entry) => getMappedTimelineEntry(entry));
|
|
74
|
-
}
|
|
75
|
-
// Trim silent samples from start and end of mapped entry range
|
|
76
|
-
const sourceSamplesPerFrame = Math.floor(sourceRawAudio.sampleRate / framesPerSecond);
|
|
77
|
-
let startSampleIndex = mappedStartFrameIndex * sourceSamplesPerFrame;
|
|
78
|
-
let endSampleIndex = mappedEndFrameIndex * sourceSamplesPerFrame;
|
|
79
|
-
const frameSamples = sourceRawAudio.audioChannels[0].subarray(startSampleIndex, endSampleIndex);
|
|
80
|
-
const silenceThresholdDecibels = -40;
|
|
81
|
-
startSampleIndex += getStartingSilentSampleCount(frameSamples, silenceThresholdDecibels);
|
|
82
|
-
endSampleIndex -= getEndingSilentSampleCount(frameSamples, silenceThresholdDecibels);
|
|
83
|
-
endSampleIndex = Math.max(endSampleIndex, startSampleIndex);
|
|
84
|
-
// Build mapped timeline entry
|
|
85
|
-
const startTime = startSampleIndex / sourceRawAudio.sampleRate;
|
|
86
|
-
const endTime = endSampleIndex / sourceRawAudio.sampleRate;
|
|
87
|
-
return {
|
|
88
|
-
type: timelineEntry.type,
|
|
89
|
-
text: timelineEntry.text,
|
|
90
|
-
startTime,
|
|
91
|
-
endTime,
|
|
92
|
-
timeline: innerTimeline
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
const mappedTimeline = referenceTimeline.map((timelineEntry) => getMappedTimelineEntry(timelineEntry));
|
|
67
|
+
const mappedTimeline = referenceTimeline.map(entry => getMappedTimelineEntry(entry, sourceRawAudio, framesPerSecond, compactedPath));
|
|
96
68
|
logger.end();
|
|
97
69
|
return mappedTimeline;
|
|
98
70
|
}
|
|
@@ -201,6 +173,103 @@ export async function alignUsingDtwWithRecognition(sourceRawAudio, referenceRawA
|
|
|
201
173
|
logger.end();
|
|
202
174
|
return result;
|
|
203
175
|
}
|
|
176
|
+
// This is experimental code. It doesn't work well enough to be usable for anything.
|
|
177
|
+
// Just testing some alternative approaches.
|
|
178
|
+
export async function alignUsingDtwWithEmbeddings(sourceRawAudio, referenceRawAudio, referenceTimeline, language, granularities, windowDurations) {
|
|
179
|
+
const logger = new Logger();
|
|
180
|
+
if (sourceRawAudio.sampleRate != 16000) {
|
|
181
|
+
throw new Error('Source audio must have a sample rate of 16000 Hz');
|
|
182
|
+
}
|
|
183
|
+
if (referenceRawAudio.sampleRate != 16000) {
|
|
184
|
+
throw new Error('Reference audio must have a sample rate of 16000 Hz');
|
|
185
|
+
}
|
|
186
|
+
const embeddingType = 'w2v-bert-2.0';
|
|
187
|
+
let sourceEmbeddings;
|
|
188
|
+
let referenceEmbeddings;
|
|
189
|
+
let framesPerSecond;
|
|
190
|
+
if (embeddingType === 'w2v-bert-2.0') {
|
|
191
|
+
const packageName = 'w2v-bert-2.0-uint8';
|
|
192
|
+
const modelDir = await loadPackage(packageName);
|
|
193
|
+
const modelFilePath = path.join(modelDir, `${packageName}.onnx`);
|
|
194
|
+
const { Wav2Vec2BertFeatureEmbeddings } = await import('../speech-embeddings/WavToVec2BertFeatureEmbeddings.js');
|
|
195
|
+
const wav2vecBert = new Wav2Vec2BertFeatureEmbeddings(modelFilePath, ['cpu']);
|
|
196
|
+
logger.start(`Extract source audio embeddings using the W2V-BERT-2.0 model`);
|
|
197
|
+
sourceEmbeddings = await wav2vecBert.computeEmbeddings(sourceRawAudio);
|
|
198
|
+
logger.start(`Extract reference audio embeddings using the W2V-BERT-2.0 model`);
|
|
199
|
+
referenceEmbeddings = await wav2vecBert.computeEmbeddings(referenceRawAudio);
|
|
200
|
+
framesPerSecond = 1000 / 10 / 2;
|
|
201
|
+
}
|
|
202
|
+
else if (embeddingType === 'whisper') {
|
|
203
|
+
const sourceSamples = sourceRawAudio.audioChannels[0];
|
|
204
|
+
const referenceSamples = referenceRawAudio.audioChannels[0];
|
|
205
|
+
const WhisperSTT = await import(`../recognition/WhisperSTT.js`);
|
|
206
|
+
const { modelName, modelDir } = await WhisperSTT.loadPackagesAndGetPaths('base.en', language);
|
|
207
|
+
const whisper = new WhisperSTT.Whisper(modelName, modelDir, ['dml', 'cpu'], ['cpu']);
|
|
208
|
+
async function encodeToAudioFeatures(samples) {
|
|
209
|
+
const featureVectors = [];
|
|
210
|
+
for (let i = 0; i < samples.length; i += 16000 * 30) {
|
|
211
|
+
const startSampleIndex = i;
|
|
212
|
+
const endSampleIndex = Math.min(samples.length, i + 16000 * 30);
|
|
213
|
+
const partSampleCount = endSampleIndex - startSampleIndex;
|
|
214
|
+
const audioPart = samples.subarray(startSampleIndex, endSampleIndex);
|
|
215
|
+
const rawAudioForPart = { audioChannels: [audioPart], sampleRate: 16000 };
|
|
216
|
+
const resultTensor = await whisper.encodeAudio(rawAudioForPart);
|
|
217
|
+
const vectorLength = resultTensor.dims[2];
|
|
218
|
+
let featureVectorsForPart = splitFloat32Array(resultTensor.data, vectorLength);
|
|
219
|
+
featureVectorsForPart = featureVectorsForPart.slice(0, Math.floor((partSampleCount / (16000 * 30)) * 1500));
|
|
220
|
+
featureVectors.push(...featureVectorsForPart);
|
|
221
|
+
}
|
|
222
|
+
return featureVectors;
|
|
223
|
+
}
|
|
224
|
+
logger.start(`Extract source audio embeddings using the Whisper encoder model`);
|
|
225
|
+
sourceEmbeddings = await encodeToAudioFeatures(sourceSamples);
|
|
226
|
+
logger.start(`Extract reference audio embeddings using the Whisper encoder model`);
|
|
227
|
+
referenceEmbeddings = await encodeToAudioFeatures(referenceSamples);
|
|
228
|
+
framesPerSecond = 1500 / 30;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
throw new Error(`Unknown embedding type: ${embeddingType}`);
|
|
232
|
+
}
|
|
233
|
+
logger.start(`Align source and reference audio embeddings using DTW`);
|
|
234
|
+
const { path: alignmentPath } = alignDTWWindowed(referenceEmbeddings, sourceEmbeddings, cosineDistance, 1000 * 1000);
|
|
235
|
+
const compactedPath = compactPath(alignmentPath);
|
|
236
|
+
logger.start('\nConvert path to timeline');
|
|
237
|
+
const mappedTimeline = referenceTimeline.map(entry => getMappedTimelineEntry(entry, sourceRawAudio, framesPerSecond, compactedPath));
|
|
238
|
+
logger.end();
|
|
239
|
+
return mappedTimeline;
|
|
240
|
+
}
|
|
241
|
+
function getMappedTimelineEntry(timelineEntry, sourceRawAudio, framesPerSecond, compactedPath, recurse = true) {
|
|
242
|
+
const referenceStartFrameIndex = Math.floor(timelineEntry.startTime * framesPerSecond);
|
|
243
|
+
const referenceEndFrameIndex = Math.floor(timelineEntry.endTime * framesPerSecond);
|
|
244
|
+
if (referenceStartFrameIndex < 0 || referenceEndFrameIndex < 0) {
|
|
245
|
+
throw new Error('Unexpected: encountered a negative timestamp in timeline');
|
|
246
|
+
}
|
|
247
|
+
const mappedStartFrameIndex = getMappedFrameIndexForPath(referenceStartFrameIndex, compactedPath, 'first');
|
|
248
|
+
const mappedEndFrameIndex = getMappedFrameIndexForPath(referenceEndFrameIndex, compactedPath, 'first');
|
|
249
|
+
let innerTimeline;
|
|
250
|
+
if (recurse && timelineEntry.timeline != null) {
|
|
251
|
+
innerTimeline = timelineEntry.timeline.map((entry) => getMappedTimelineEntry(entry, sourceRawAudio, framesPerSecond, compactedPath, recurse));
|
|
252
|
+
}
|
|
253
|
+
// Trim silent samples from start and end of mapped entry range
|
|
254
|
+
const sourceSamplesPerFrame = Math.floor(sourceRawAudio.sampleRate / framesPerSecond);
|
|
255
|
+
let startSampleIndex = mappedStartFrameIndex * sourceSamplesPerFrame;
|
|
256
|
+
let endSampleIndex = mappedEndFrameIndex * sourceSamplesPerFrame;
|
|
257
|
+
const frameSamples = sourceRawAudio.audioChannels[0].subarray(startSampleIndex, endSampleIndex);
|
|
258
|
+
const silenceThresholdDecibels = -40;
|
|
259
|
+
startSampleIndex += getStartingSilentSampleCount(frameSamples, silenceThresholdDecibels);
|
|
260
|
+
endSampleIndex -= getEndingSilentSampleCount(frameSamples, silenceThresholdDecibels);
|
|
261
|
+
endSampleIndex = Math.max(endSampleIndex, startSampleIndex);
|
|
262
|
+
// Build mapped timeline entry
|
|
263
|
+
const startTime = startSampleIndex / sourceRawAudio.sampleRate;
|
|
264
|
+
const endTime = endSampleIndex / sourceRawAudio.sampleRate;
|
|
265
|
+
return {
|
|
266
|
+
type: timelineEntry.type,
|
|
267
|
+
text: timelineEntry.text,
|
|
268
|
+
startTime,
|
|
269
|
+
endTime,
|
|
270
|
+
timeline: innerTimeline
|
|
271
|
+
};
|
|
272
|
+
}
|
|
204
273
|
export async function interpolatePhoneTimelines(sourceTimeline, referenceTimeline) {
|
|
205
274
|
const interpolatedTimeline = [];
|
|
206
275
|
for (let wordEntryIndex = 0; wordEntryIndex < sourceTimeline.length; wordEntryIndex++) {
|
|
@@ -288,7 +357,29 @@ export async function createAlignmentReferenceUsingEspeakForFragments(fragments,
|
|
|
288
357
|
progressLogger.start("Load espeak module");
|
|
289
358
|
const Espeak = await import("../synthesis/EspeakTTS.js");
|
|
290
359
|
progressLogger.start("Synthesize alignment reference with eSpeak");
|
|
291
|
-
const result =
|
|
360
|
+
const result = {
|
|
361
|
+
rawAudio: getEmptyRawAudio(1, await Espeak.getSampleRate()),
|
|
362
|
+
timeline: [],
|
|
363
|
+
events: [],
|
|
364
|
+
};
|
|
365
|
+
// Split fragments to chunks and process each chunk individually,
|
|
366
|
+
// and incrementally merge the chunks to the final result.
|
|
367
|
+
{
|
|
368
|
+
const maxFragmentsInChunk = 1000;
|
|
369
|
+
let timeOffset = 0;
|
|
370
|
+
for (let startOffset = 0; startOffset < fragments.length; startOffset += maxFragmentsInChunk) {
|
|
371
|
+
const chunk = fragments.slice(startOffset, startOffset + maxFragmentsInChunk);
|
|
372
|
+
const chunkResult = await Espeak.synthesizeFragments(chunk, espeakOptions);
|
|
373
|
+
result.rawAudio = {
|
|
374
|
+
sampleRate: result.rawAudio.sampleRate,
|
|
375
|
+
audioChannels: concatAudioSegments([result.rawAudio.audioChannels, chunkResult.rawAudio.audioChannels])
|
|
376
|
+
};
|
|
377
|
+
const chunkTimeline = addTimeOffsetToTimeline(chunkResult.timeline, timeOffset);
|
|
378
|
+
result.timeline = [...result.timeline, ...chunkTimeline];
|
|
379
|
+
result.events = [...result.events, ...chunkResult.events];
|
|
380
|
+
timeOffset += getRawAudioDuration(chunkResult.rawAudio);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
292
383
|
result.timeline = result.timeline.flatMap(clause => clause.timeline);
|
|
293
384
|
for (const wordEntry of result.timeline) {
|
|
294
385
|
wordEntry.timeline = wordEntry.timeline.flatMap(tokenEntry => tokenEntry.timeline);
|
|
@@ -296,7 +387,7 @@ export async function createAlignmentReferenceUsingEspeakForFragments(fragments,
|
|
|
296
387
|
progressLogger.end();
|
|
297
388
|
return result;
|
|
298
389
|
}
|
|
299
|
-
export async function createAlignmentReferenceUsingEspeak(transcript, language, plaintextOptions, customLexiconPaths, insertSeparators) {
|
|
390
|
+
export async function createAlignmentReferenceUsingEspeak(transcript, language, plaintextOptions, customLexiconPaths, insertSeparators, useKlatt) {
|
|
300
391
|
const logger = new Logger();
|
|
301
392
|
logger.start('Synthesize alignment reference with eSpeak');
|
|
302
393
|
const synthesisOptions = {
|
|
@@ -305,7 +396,7 @@ export async function createAlignmentReferenceUsingEspeak(transcript, language,
|
|
|
305
396
|
plainText: plaintextOptions,
|
|
306
397
|
customLexiconPaths: customLexiconPaths,
|
|
307
398
|
espeak: {
|
|
308
|
-
useKlatt
|
|
399
|
+
useKlatt,
|
|
309
400
|
insertSeparators,
|
|
310
401
|
}
|
|
311
402
|
};
|