echogarden 1.0.5 → 1.1.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 +26 -23
- package/data/schemas/options.json +177 -36
- package/dist/alignment/SpeechAlignment.d.ts +1 -1
- package/dist/alignment/SpeechAlignment.js +1 -1
- package/dist/alignment/SpeechAlignment.js.map +1 -1
- package/dist/api/API.d.ts +1 -0
- package/dist/api/API.js +1 -0
- package/dist/api/API.js.map +1 -1
- package/dist/api/APIOptions.d.ts +1 -0
- package/dist/api/Alignment.d.ts +3 -3
- package/dist/api/Alignment.js +5 -10
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/LanguageDetection.d.ts +5 -7
- package/dist/api/LanguageDetection.js +3 -2
- package/dist/api/LanguageDetection.js.map +1 -1
- package/dist/api/Recognition.d.ts +4 -5
- package/dist/api/Recognition.js +5 -8
- package/dist/api/Recognition.js.map +1 -1
- package/dist/api/SourceSeparation.d.ts +2 -0
- package/dist/api/SourceSeparation.js +4 -2
- package/dist/api/SourceSeparation.js.map +1 -1
- package/dist/api/Synthesis.d.ts +3 -1
- package/dist/api/Synthesis.js +9 -10
- package/dist/api/Synthesis.js.map +1 -1
- package/dist/api/Translation.d.ts +1 -1
- package/dist/api/Translation.js +4 -8
- package/dist/api/Translation.js.map +1 -1
- package/dist/api/TranslationAlignment.d.ts +31 -0
- package/dist/api/TranslationAlignment.js +121 -0
- package/dist/api/TranslationAlignment.js.map +1 -0
- package/dist/api/VoiceActivityDetection.d.ts +5 -1
- package/dist/api/VoiceActivityDetection.js +38 -2
- package/dist/api/VoiceActivityDetection.js.map +1 -1
- package/dist/audio/AudioPlayer.js +6 -1
- package/dist/audio/AudioPlayer.js.map +1 -1
- package/dist/cli/CLI.js +85 -0
- package/dist/cli/CLI.js.map +1 -1
- package/dist/dsp/FFT.js.map +1 -1
- package/dist/math/MedianFilter.d.ts +5 -0
- package/dist/math/MedianFilter.js +102 -0
- package/dist/math/MedianFilter.js.map +1 -0
- package/dist/math/VectorMath.d.ts +0 -2
- package/dist/math/VectorMath.js +1 -25
- package/dist/math/VectorMath.js.map +1 -1
- package/dist/recognition/OpenAICloudSTT.d.ts +1 -1
- package/dist/recognition/OpenAICloudSTT.js.map +1 -1
- package/dist/recognition/SileroSTT.d.ts +22 -1
- package/dist/recognition/SileroSTT.js +122 -95
- package/dist/recognition/SileroSTT.js.map +1 -1
- package/dist/recognition/WhisperCppSTT.js +1 -1
- package/dist/recognition/WhisperCppSTT.js.map +1 -1
- package/dist/recognition/WhisperSTT.d.ts +52 -19
- package/dist/recognition/WhisperSTT.js +628 -479
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/server/Server.js.map +1 -1
- package/dist/source-separation/MDXNetSourceSeparation.d.ts +5 -3
- package/dist/source-separation/MDXNetSourceSeparation.js +26 -19
- package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
- package/dist/speech-language-detection/SileroLanguageDetection.d.ts +15 -9
- package/dist/speech-language-detection/SileroLanguageDetection.js +23 -16
- package/dist/speech-language-detection/SileroLanguageDetection.js.map +1 -1
- package/dist/synthesis/EspeakTTS.js +4 -0
- package/dist/synthesis/EspeakTTS.js.map +1 -1
- package/dist/synthesis/GoogleCloudTTS.js.map +1 -1
- package/dist/synthesis/VitsTTS.d.ts +8 -6
- package/dist/synthesis/VitsTTS.js +36 -31
- package/dist/synthesis/VitsTTS.js.map +1 -1
- package/dist/tests/Test.js.map +1 -1
- package/dist/utilities/OnnxUtilities.d.ts +14 -0
- package/dist/utilities/OnnxUtilities.js +43 -0
- package/dist/utilities/OnnxUtilities.js.map +1 -0
- package/dist/utilities/Utilities.d.ts +4 -8
- package/dist/utilities/Utilities.js +35 -58
- package/dist/utilities/Utilities.js.map +1 -1
- package/dist/voice-activity-detection/SileroVAD.d.ts +5 -3
- package/dist/voice-activity-detection/SileroVAD.js +9 -11
- package/dist/voice-activity-detection/SileroVAD.js.map +1 -1
- package/docs/API.md +54 -34
- package/docs/CLI.md +25 -13
- package/docs/Contributing.md +4 -2
- package/docs/Engines.md +43 -32
- package/docs/Licenses.md +3 -4
- package/docs/Options.md +47 -11
- package/docs/Server.md +8 -6
- package/docs/Tasklist.md +39 -52
- package/docs/Technical.md +1 -1
- package/package.json +7 -11
- package/src/alignment/SpeechAlignment.ts +1 -1
- package/src/api/API.ts +1 -0
- package/src/api/APIOptions.ts +1 -0
- package/src/api/Alignment.ts +10 -14
- package/src/api/LanguageDetection.ts +14 -10
- package/src/api/Recognition.ts +17 -10
- package/src/api/SourceSeparation.ts +7 -2
- package/src/api/Synthesis.ts +26 -11
- package/src/api/Translation.ts +14 -8
- package/src/api/TranslationAlignment.ts +213 -0
- package/src/api/VoiceActivityDetection.ts +66 -3
- package/src/audio/AudioPlayer.ts +6 -2
- package/src/cli/CLI.ts +121 -2
- package/src/dsp/FFT.ts +3 -0
- package/src/math/MedianFilter.ts +124 -0
- package/src/math/VectorMath.ts +1 -36
- package/src/recognition/OpenAICloudSTT.ts +27 -27
- package/src/recognition/SileroSTT.ts +149 -102
- package/src/recognition/WhisperCppSTT.ts +1 -1
- package/src/recognition/WhisperSTT.ts +948 -672
- package/src/server/Server.ts +1 -1
- package/src/source-separation/MDXNetSourceSeparation.ts +35 -19
- package/src/speech-language-detection/SileroLanguageDetection.ts +53 -33
- package/src/synthesis/EspeakTTS.ts +8 -0
- package/src/synthesis/GoogleCloudTTS.ts +12 -1
- package/src/synthesis/VitsTTS.ts +57 -46
- package/src/tests/Test.ts +1 -1
- package/src/utilities/OnnxUtilities.ts +68 -0
- package/src/utilities/Utilities.ts +38 -66
- package/src/voice-activity-detection/SileroVAD.ts +15 -15
- package/dist/utilities/NdArrayUtilities.d.ts +0 -3
- package/dist/utilities/NdArrayUtilities.js +0 -23
- package/dist/utilities/NdArrayUtilities.js.map +0 -1
- package/src/utilities/NdArrayUtilities.ts +0 -31
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Logger } from '../utilities/Logger.js';
|
|
2
2
|
import { computeMelSpectogramUsingFilterbanks } from '../dsp/MelSpectogram.js';
|
|
3
|
-
import { clip,
|
|
4
|
-
import { indexOfMax, logOfVector, logSumExp, meanOfVector,
|
|
3
|
+
import { clip, containsInvalidCodepoint, getIntegerRange, getTokenRepetitionScore, splitFloat32Array, yieldToEventLoop } from '../utilities/Utilities.js';
|
|
4
|
+
import { indexOfMax, logOfVector, logSumExp, meanOfVector, softmax, stdDeviationOfVector } from '../math/VectorMath.js';
|
|
5
5
|
import { alignDTWWindowed } from '../alignment/DTWSequenceAlignmentWindowed.js';
|
|
6
6
|
import { extendDeep } from '../utilities/ObjectUtilities.js';
|
|
7
|
-
import { getRawAudioDuration } from '../audio/AudioUtilities.js';
|
|
7
|
+
import { getRawAudioDuration, sliceRawAudio } from '../audio/AudioUtilities.js';
|
|
8
8
|
import { readFile } from '../utilities/FileSystem.js';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import { getShortLanguageCode, languageCodeToName } from '../utilities/Locale.js';
|
|
@@ -12,8 +12,12 @@ import { loadPackage } from '../utilities/PackageManager.js';
|
|
|
12
12
|
import chalk from 'chalk';
|
|
13
13
|
import { XorShift32RNG } from '../utilities/RandomGenerator.js';
|
|
14
14
|
import { detectSpeechLanguageByParts } from '../api/LanguageDetection.js';
|
|
15
|
-
import { isPunctuation, isWhitespace } from '../nlp/Segmentation.js';
|
|
15
|
+
import { isPunctuation, isWhitespace, isWord, splitToSentences, splitToWords } from '../nlp/Segmentation.js';
|
|
16
|
+
import { medianOf5Filter } from '../math/MedianFilter.js';
|
|
17
|
+
import { getDeflateCompressionMetricsForString } from '../utilities/Compression.js';
|
|
18
|
+
import { getOnnxSessionOptions, makeOnnxLikeFloat32Tensor } from '../utilities/OnnxUtilities.js';
|
|
16
19
|
export async function recognize(sourceRawAudio, modelName, modelDir, task, sourceLanguage, options) {
|
|
20
|
+
options = extendDeep(defaultWhisperOptions, options);
|
|
17
21
|
if (sourceRawAudio.sampleRate != 16000) {
|
|
18
22
|
throw new Error('Source audio must have a sampling rate of 16000');
|
|
19
23
|
}
|
|
@@ -31,11 +35,14 @@ export async function recognize(sourceRawAudio, modelName, modelDir, task, sourc
|
|
|
31
35
|
if (seed) {
|
|
32
36
|
seed = Math.max(Math.floor(seed), 1) | 0;
|
|
33
37
|
}
|
|
34
|
-
const
|
|
38
|
+
const encoderProviders = options.encoderProvider ? [options.encoderProvider] : ['dml', 'cpu'];
|
|
39
|
+
const decoderProviders = options.decoderProvider ? [options.decoderProvider] : [];
|
|
40
|
+
const whisper = new Whisper(modelName, modelDir, encoderProviders, decoderProviders, seed);
|
|
35
41
|
const result = await whisper.recognize(sourceRawAudio, task, sourceLanguage, options);
|
|
36
42
|
return result;
|
|
37
43
|
}
|
|
38
|
-
export async function align(sourceRawAudio,
|
|
44
|
+
export async function align(sourceRawAudio, transcript, modelName, modelDir, sourceLanguage, options) {
|
|
45
|
+
options = extendDeep(defaultWhisperAlignmentOptions, options);
|
|
39
46
|
if (sourceRawAudio.sampleRate != 16000) {
|
|
40
47
|
throw new Error('Source audio must have a sampling rate of 16000');
|
|
41
48
|
}
|
|
@@ -46,46 +53,101 @@ export async function align(sourceRawAudio, referenceText, modelName, modelDir,
|
|
|
46
53
|
if (isEnglishOnlyModel(modelName) && sourceLanguage != 'en') {
|
|
47
54
|
throw new Error(`The model '${modelName}' can only be used with English inputs. However, the given source language was ${languageCodeToName(sourceLanguage)}.`);
|
|
48
55
|
}
|
|
49
|
-
const
|
|
50
|
-
const
|
|
56
|
+
const encoderProviders = options.encoderProvider ? [options.encoderProvider] : ['dml', 'cpu'];
|
|
57
|
+
const decoderProviders = options.decoderProvider ? [options.decoderProvider] : [];
|
|
58
|
+
const whisper = new Whisper(modelName, modelDir, encoderProviders, decoderProviders);
|
|
59
|
+
const timeline = await whisper.align(sourceRawAudio, transcript, sourceLanguage, 'transcribe', options);
|
|
51
60
|
return timeline;
|
|
52
61
|
}
|
|
53
|
-
export async function
|
|
62
|
+
export async function alignEnglishTranslation(sourceRawAudio, translatedTranscript, modelName, modelDir, sourceLanguage, options) {
|
|
63
|
+
options = extendDeep(defaultWhisperAlignmentOptions, options);
|
|
64
|
+
if (sourceRawAudio.sampleRate != 16000) {
|
|
65
|
+
throw new Error('Source audio must have a sampling rate of 16000');
|
|
66
|
+
}
|
|
67
|
+
sourceLanguage = getShortLanguageCode(sourceLanguage);
|
|
68
|
+
if (!(sourceLanguage in languageIdLookup)) {
|
|
69
|
+
throw new Error(`The source language ${languageCodeToName(sourceLanguage)} is not supported by the Whisper engine.`);
|
|
70
|
+
}
|
|
71
|
+
if (isEnglishOnlyModel(modelName)) {
|
|
72
|
+
throw new Error(`Translation alignment can only be done with multilingual models.`);
|
|
73
|
+
}
|
|
74
|
+
const encoderProviders = options.encoderProvider ? [options.encoderProvider] : ['dml', 'cpu'];
|
|
75
|
+
const decoderProviders = options.decoderProvider ? [options.decoderProvider] : [];
|
|
76
|
+
const whisper = new Whisper(modelName, modelDir, encoderProviders, decoderProviders);
|
|
77
|
+
const timeline = await whisper.align(sourceRawAudio, translatedTranscript, sourceLanguage, 'translate', options);
|
|
78
|
+
return timeline;
|
|
79
|
+
}
|
|
80
|
+
export async function detectLanguage(sourceRawAudio, modelName, modelDir, options) {
|
|
81
|
+
options = extendDeep(defaultWhisperLanguageDetectionOptions, options);
|
|
54
82
|
if (sourceRawAudio.sampleRate != 16000) {
|
|
55
83
|
throw new Error('Source audio must have a sampling rate of 16000');
|
|
56
84
|
}
|
|
57
85
|
if (!isMultilingualModel(modelName)) {
|
|
58
86
|
throw new Error(`Language detection is only supported with multilingual models.`);
|
|
59
87
|
}
|
|
60
|
-
if (temperature < 0) {
|
|
88
|
+
if (options.temperature < 0) {
|
|
61
89
|
throw new Error(`Temperature cannot be negative`);
|
|
62
90
|
}
|
|
63
|
-
const
|
|
91
|
+
const encoderProviders = options.encoderProvider ? [options.encoderProvider] : ['dml', 'cpu'];
|
|
92
|
+
const decoderProviders = options.decoderProvider ? [options.decoderProvider] : [];
|
|
93
|
+
const whisper = new Whisper(modelName, modelDir, encoderProviders, decoderProviders);
|
|
64
94
|
async function detectLanguageForPart(partAudio) {
|
|
65
95
|
const audioFeatures = await whisper.encodeAudio(partAudio);
|
|
66
|
-
const partResults = await whisper.detectLanguage(audioFeatures, temperature);
|
|
96
|
+
const partResults = await whisper.detectLanguage(audioFeatures, options.temperature);
|
|
67
97
|
return partResults;
|
|
68
98
|
}
|
|
69
99
|
const results = await detectSpeechLanguageByParts(sourceRawAudio, detectLanguageForPart);
|
|
70
100
|
results.sort((entry1, entry2) => entry2.probability - entry1.probability);
|
|
71
101
|
return results;
|
|
72
102
|
}
|
|
103
|
+
export async function detectVoiceActivity(sourceRawAudio, modelName, modelDir, options) {
|
|
104
|
+
options = extendDeep(defaultWhisperVADOptions, options);
|
|
105
|
+
if (sourceRawAudio.sampleRate != 16000) {
|
|
106
|
+
throw new Error('Source audio must have a sampling rate of 16000');
|
|
107
|
+
}
|
|
108
|
+
if (options.temperature < 0) {
|
|
109
|
+
throw new Error(`Temperature cannot be negative`);
|
|
110
|
+
}
|
|
111
|
+
const audioSamples = sourceRawAudio.audioChannels[0];
|
|
112
|
+
const partDuration = 5;
|
|
113
|
+
const maxSamplesCountForPart = sourceRawAudio.sampleRate * partDuration;
|
|
114
|
+
const encoderProviders = options.encoderProvider ? [options.encoderProvider] : ['dml', 'cpu'];
|
|
115
|
+
const decoderProviders = options.decoderProvider ? [options.decoderProvider] : [];
|
|
116
|
+
const whisper = new Whisper(modelName, modelDir, encoderProviders, decoderProviders);
|
|
117
|
+
const partProbabilities = [];
|
|
118
|
+
for (let sampleOffset = 0; sampleOffset < audioSamples.length; sampleOffset += maxSamplesCountForPart) {
|
|
119
|
+
const partSamples = sliceRawAudio(sourceRawAudio, sampleOffset, sampleOffset + maxSamplesCountForPart);
|
|
120
|
+
const samplesCountForPart = partSamples.audioChannels[0].length;
|
|
121
|
+
const startTime = sampleOffset / sourceRawAudio.sampleRate;
|
|
122
|
+
const endTime = (sampleOffset + samplesCountForPart) / sourceRawAudio.sampleRate;
|
|
123
|
+
const encodedPartSamples = await whisper.encodeAudio(partSamples);
|
|
124
|
+
const probabilityForPart = await whisper.detectVoiceActivity(encodedPartSamples, options.temperature);
|
|
125
|
+
partProbabilities.push({
|
|
126
|
+
type: 'segment',
|
|
127
|
+
text: '',
|
|
128
|
+
startTime,
|
|
129
|
+
endTime,
|
|
130
|
+
confidence: probabilityForPart,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return { partProbabilities };
|
|
134
|
+
}
|
|
73
135
|
export class Whisper {
|
|
74
136
|
modelName;
|
|
75
137
|
modelDir;
|
|
138
|
+
encoderExecutionProviders;
|
|
139
|
+
decoderExecutionProviders;
|
|
76
140
|
isMultiligualModel;
|
|
77
141
|
audioEncoder;
|
|
78
142
|
textDecoder;
|
|
79
143
|
tiktoken;
|
|
80
|
-
onnxOptions = {
|
|
81
|
-
logSeverityLevel: 2,
|
|
82
|
-
executionProviders: ['cpu']
|
|
83
|
-
};
|
|
84
144
|
tokenConfig;
|
|
85
145
|
randomGen;
|
|
86
|
-
constructor(modelName, modelDir, rngSeed = 461845907) {
|
|
146
|
+
constructor(modelName, modelDir, encoderExecutionProviders, decoderExecutionProviders, rngSeed = 461845907) {
|
|
87
147
|
this.modelName = modelName;
|
|
88
148
|
this.modelDir = modelDir;
|
|
149
|
+
this.encoderExecutionProviders = encoderExecutionProviders;
|
|
150
|
+
this.decoderExecutionProviders = decoderExecutionProviders;
|
|
89
151
|
this.isMultiligualModel = isMultilingualModel(this.modelName);
|
|
90
152
|
if (this.isMultiligualModel) {
|
|
91
153
|
this.tokenConfig = {
|
|
@@ -119,87 +181,25 @@ export class Whisper {
|
|
|
119
181
|
}
|
|
120
182
|
this.randomGen = new XorShift32RNG(rngSeed);
|
|
121
183
|
}
|
|
122
|
-
async
|
|
123
|
-
await this.initializeTokenizerIfNeeded();
|
|
124
|
-
await this.initializeEncoderSessionIfNeeded();
|
|
125
|
-
await this.initializeDecoderSessionIfNeeded();
|
|
126
|
-
}
|
|
127
|
-
async initializeTokenizerIfNeeded() {
|
|
128
|
-
if (this.tiktoken) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
const logger = new Logger();
|
|
132
|
-
await logger.startAsync('Load tokenizer data');
|
|
133
|
-
const tiktokenModulePackagePath = await loadPackage('whisper-tiktoken-data');
|
|
134
|
-
const tiktokenDataFilePath = path.join(tiktokenModulePackagePath, this.isMultiligualModel ? 'multilingual.tiktoken' : 'gpt2.tiktoken');
|
|
135
|
-
let tiktokenData = await readFile(tiktokenDataFilePath, { encoding: 'utf8' });
|
|
136
|
-
const tokenConfig = this.tokenConfig;
|
|
137
|
-
const metadataTokens = {
|
|
138
|
-
[tokenConfig.endOfTextToken]: '[EndOfText]',
|
|
139
|
-
[tokenConfig.startOfTextToken]: '[StartOfText]',
|
|
140
|
-
[tokenConfig.translateTaskToken]: '[TranslateTask]',
|
|
141
|
-
[tokenConfig.transcribeTaskToken]: '[TranscribeTask]',
|
|
142
|
-
[tokenConfig.startOfPromptToken]: '[StartOfPrompt]',
|
|
143
|
-
[tokenConfig.nonSpeechToken]: '[NonSpeech]',
|
|
144
|
-
[tokenConfig.noTimestampsToken]: '[NoTimestamps]',
|
|
145
|
-
};
|
|
146
|
-
if (this.isMultiligualModel) {
|
|
147
|
-
metadataTokens[50256] = '[Unused_50256]';
|
|
148
|
-
metadataTokens[50360] = '[Unused_50360]';
|
|
149
|
-
}
|
|
150
|
-
const languageTokenCount = tokenConfig.languageTokensEnd - tokenConfig.languageTokensStart;
|
|
151
|
-
for (let i = 0; i < languageTokenCount; i++) {
|
|
152
|
-
const tokenIndex = this.tokenConfig.languageTokensStart + i;
|
|
153
|
-
metadataTokens[tokenIndex] = `[Language_${i}]`;
|
|
154
|
-
}
|
|
155
|
-
const timestampTokensCount = 1501;
|
|
156
|
-
for (let i = 0; i < timestampTokensCount; i++) {
|
|
157
|
-
const tokenIndex = this.tokenConfig.timestampTokensStart + i;
|
|
158
|
-
const tokenTime = this.timestampTokenToSeconds(tokenIndex);
|
|
159
|
-
metadataTokens[tokenIndex] = `[Timestamp_${tokenTime.toFixed(2)}]`;
|
|
160
|
-
}
|
|
161
|
-
const inverseMetadataTokensLookup = {};
|
|
162
|
-
for (const [key, value] of Object.entries(metadataTokens)) {
|
|
163
|
-
inverseMetadataTokensLookup[value] = parseInt(key);
|
|
164
|
-
}
|
|
165
|
-
const patternString = `'s|'t|'re|'ve|'m|'ll|'d| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)|\\s+`;
|
|
166
|
-
const { Tiktoken } = await import('tiktoken/lite');
|
|
167
|
-
this.tiktoken = new Tiktoken(tiktokenData, inverseMetadataTokensLookup, patternString);
|
|
168
|
-
logger.end();
|
|
169
|
-
}
|
|
170
|
-
async initializeEncoderSessionIfNeeded() {
|
|
171
|
-
if (this.audioEncoder) {
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
const logger = new Logger();
|
|
175
|
-
await logger.startAsync(`Create encoder model inference session for model '${this.modelName}'`);
|
|
176
|
-
const encoderFilePath = path.join(this.modelDir, 'encoder.onnx');
|
|
177
|
-
const Onnx = await import('onnxruntime-node');
|
|
178
|
-
this.audioEncoder = await Onnx.InferenceSession.create(encoderFilePath, this.onnxOptions);
|
|
179
|
-
logger.end();
|
|
180
|
-
}
|
|
181
|
-
async initializeDecoderSessionIfNeeded() {
|
|
182
|
-
if (this.textDecoder) {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const logger = new Logger();
|
|
186
|
-
await logger.startAsync(`Create decoder model inference session for model '${this.modelName}'`);
|
|
187
|
-
const decoderFilePath = path.join(this.modelDir, 'decoder.onnx');
|
|
188
|
-
const Onnx = await import('onnxruntime-node');
|
|
189
|
-
this.textDecoder = await Onnx.InferenceSession.create(decoderFilePath, this.onnxOptions);
|
|
190
|
-
logger.end();
|
|
191
|
-
}
|
|
192
|
-
async recognize(rawAudio, task, language, options) {
|
|
184
|
+
async recognize(rawAudio, task, language, options, logitFilter) {
|
|
193
185
|
await this.initializeIfNeeded();
|
|
194
186
|
const logger = new Logger();
|
|
187
|
+
options = extendDeep(defaultWhisperOptions, options);
|
|
188
|
+
options.model = this.modelName;
|
|
195
189
|
const audioSamples = rawAudio.audioChannels[0];
|
|
196
190
|
const sampleRate = rawAudio.sampleRate;
|
|
197
191
|
const prompt = options.prompt;
|
|
198
|
-
const maxAudioSamplesPerPart = sampleRate * 30;
|
|
199
192
|
const decodeTimestampTokens = options.decodeTimestampTokens;
|
|
193
|
+
const maxAudioSamplesPerPart = sampleRate * 30;
|
|
200
194
|
let previousPartTextTokens = [];
|
|
201
195
|
let timeline = [];
|
|
202
196
|
let allDecodedTokens = [];
|
|
197
|
+
let wrappedLogitFilter;
|
|
198
|
+
if (logitFilter) {
|
|
199
|
+
wrappedLogitFilter = (logits, partDecodedTokens, isFirstPart, isFinalPart) => {
|
|
200
|
+
return logitFilter(logits, [...allDecodedTokens, ...partDecodedTokens], isFirstPart, isFinalPart);
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
203
|
for (let audioOffset = 0; audioOffset < audioSamples.length;) {
|
|
204
204
|
const segmentStartTime = audioOffset / sampleRate;
|
|
205
205
|
await logger.startAsync(`\nPrepare audio part at time position ${segmentStartTime.toFixed(2)}`, undefined, chalk.magentaBright);
|
|
@@ -220,7 +220,7 @@ export class Whisper {
|
|
|
220
220
|
}
|
|
221
221
|
initialTokens = [...initialTokens, ...this.getTextStartTokens(language, task, !decodeTimestampTokens)];
|
|
222
222
|
logger.end();
|
|
223
|
-
let { decodedTokens: partTokens,
|
|
223
|
+
let { decodedTokens: partTokens, decodedTokensConfidence: partTokensConfidence, decodedTokensCrossAttentionQKs: partCrossAttentionQKs, } = await this.decodeTokens(audioPartFeatures, initialTokens, audioPartDuration, isFirstPart, isFinalPart, options, wrappedLogitFilter);
|
|
224
224
|
const lastToken = partTokens[partTokens.length - 1];
|
|
225
225
|
const lastTokenIsTimestamp = this.isTimestampToken(lastToken);
|
|
226
226
|
let audioEndOffset;
|
|
@@ -237,50 +237,96 @@ export class Whisper {
|
|
|
237
237
|
if (partTokens.length != partCrossAttentionQKs.length) {
|
|
238
238
|
throw new Error('Unexpected: partTokens.length != partCrossAttentionQKs.length');
|
|
239
239
|
}
|
|
240
|
+
// Prepare tokens
|
|
240
241
|
partTokens = partTokens.slice(initialTokens.length);
|
|
241
|
-
|
|
242
|
+
partTokensConfidence = partTokensConfidence.slice(initialTokens.length);
|
|
242
243
|
partCrossAttentionQKs = partCrossAttentionQKs.slice(initialTokens.length);
|
|
244
|
+
// Compute compression ratio for part
|
|
245
|
+
if (false) {
|
|
246
|
+
const compressionRatioForPart = (await getDeflateCompressionMetricsForString(this.tokensToText(partTokens))).ratio;
|
|
247
|
+
}
|
|
248
|
+
// Find alignment path
|
|
243
249
|
const alignmentPath = await this.findAlignmentPathFromQKs(partCrossAttentionQKs, partTokens, 0, segmentFrameCount); //, alignmentHeadsIndexes[this.modelName])
|
|
244
|
-
|
|
245
|
-
|
|
250
|
+
// Generate timeline from alignment path
|
|
251
|
+
const partTimeline = await this.getTokenTimelineFromAlignmentPath(alignmentPath, partTokens, segmentStartTime, segmentEndTime, partTokensConfidence);
|
|
246
252
|
allDecodedTokens.push(...partTokens);
|
|
247
253
|
timeline.push(...partTimeline);
|
|
248
254
|
previousPartTextTokens = partTokens.filter(token => this.isTextToken(token));
|
|
255
|
+
audioOffset = audioEndOffset;
|
|
249
256
|
logger.end();
|
|
250
257
|
}
|
|
258
|
+
// Convert token timeline to word timeline
|
|
251
259
|
timeline = this.tokenTimelineToWordTimeline(timeline, language);
|
|
260
|
+
// Convert tokens to transcript
|
|
252
261
|
const transcript = this.tokensToText(allDecodedTokens).trim();
|
|
253
262
|
logger.end();
|
|
254
263
|
return { transcript, timeline };
|
|
255
264
|
}
|
|
256
|
-
async align(rawAudio,
|
|
257
|
-
await this.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
265
|
+
async align(rawAudio, transcript, sourceLanguage, task, whisperAlignmentOptions) {
|
|
266
|
+
await this.initializeTokenizerIfNeeded();
|
|
267
|
+
whisperAlignmentOptions = extendDeep(defaultWhisperAlignmentOptions, whisperAlignmentOptions);
|
|
268
|
+
const shouldSplitToSentences = false;
|
|
269
|
+
let simplifiedTranscript = '';
|
|
270
|
+
if (shouldSplitToSentences) {
|
|
271
|
+
const sentences = splitToSentences(transcript, 'en');
|
|
272
|
+
for (const sentence of sentences) {
|
|
273
|
+
let sentenceWords = await splitToWords(sentence, 'en');
|
|
274
|
+
sentenceWords = sentenceWords.filter(word => isWord(word));
|
|
275
|
+
simplifiedTranscript += sentenceWords.join(' ');
|
|
276
|
+
simplifiedTranscript += ' ';
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
let words = await splitToWords(transcript, 'en');
|
|
281
|
+
words = words.map(word => word.trim());
|
|
282
|
+
words = words.filter(word => isWord(word));
|
|
283
|
+
simplifiedTranscript = words.join(' ');
|
|
284
|
+
}
|
|
285
|
+
// Tokenize the transcript
|
|
286
|
+
const simplifiedTranscriptTokens = this.textToTokens(simplifiedTranscript);
|
|
287
|
+
// Initialize custom logit filter that allows only the transcript tokens to be decoded
|
|
288
|
+
// in order.
|
|
264
289
|
const endOfTextToken = this.tokenConfig.endOfTextToken;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
290
|
+
const logitFilter = (logits, decodedTokens, isFirstPart, isFinalPart) => {
|
|
291
|
+
const decodedTextTokens = decodedTokens.filter(token => this.isTextToken(token));
|
|
292
|
+
const nextTokenToDecode = simplifiedTranscriptTokens[decodedTextTokens.length] ?? endOfTextToken;
|
|
293
|
+
const newLogits = logits.map((logit, index) => {
|
|
294
|
+
if (index === nextTokenToDecode) {
|
|
295
|
+
return logit;
|
|
296
|
+
}
|
|
297
|
+
// If it's the final part, the ent-of-text token logit is set to -Infinity.
|
|
298
|
+
// This will force to force all transcript tokens to be decoded even if the model doesn't
|
|
299
|
+
// recognize them.
|
|
300
|
+
if (!isFinalPart && index === endOfTextToken) {
|
|
301
|
+
return logit;
|
|
302
|
+
}
|
|
303
|
+
return -Infinity;
|
|
304
|
+
});
|
|
305
|
+
return newLogits;
|
|
306
|
+
};
|
|
307
|
+
// Set options for alignment
|
|
308
|
+
const options = {
|
|
309
|
+
model: this.modelName,
|
|
310
|
+
temperature: 0.0,
|
|
311
|
+
prompt: undefined,
|
|
312
|
+
topCandidateCount: 1,
|
|
313
|
+
punctuationThreshold: Infinity,
|
|
314
|
+
autoPromptParts: false,
|
|
315
|
+
maxTokensPerPart: Infinity,
|
|
316
|
+
suppressRepetition: false,
|
|
317
|
+
decodeTimestampTokens: true,
|
|
318
|
+
endTokenThreshold: whisperAlignmentOptions.endTokenThreshold,
|
|
319
|
+
includeEndTokenInCandidates: false,
|
|
320
|
+
seed: undefined,
|
|
321
|
+
};
|
|
322
|
+
// Recognize
|
|
323
|
+
const { timeline } = await this.recognize(rawAudio, task, sourceLanguage, options, logitFilter);
|
|
324
|
+
return timeline;
|
|
278
325
|
}
|
|
279
326
|
async detectLanguage(audioFeatures, temperature) {
|
|
280
327
|
if (!this.isMultiligualModel) {
|
|
281
328
|
throw new Error('Language detection is only supported with multilingual models');
|
|
282
329
|
}
|
|
283
|
-
await this.initializeTokenizerIfNeeded();
|
|
284
330
|
await this.initializeDecoderSessionIfNeeded();
|
|
285
331
|
// Prepare and run decoder
|
|
286
332
|
const logger = new Logger();
|
|
@@ -317,38 +363,76 @@ export class Whisper {
|
|
|
317
363
|
logger.end();
|
|
318
364
|
return results;
|
|
319
365
|
}
|
|
320
|
-
async
|
|
366
|
+
async detectVoiceActivity(audioFeatures, temperature) {
|
|
367
|
+
await this.initializeDecoderSessionIfNeeded();
|
|
368
|
+
// Prepare and run decoder
|
|
369
|
+
const logger = new Logger();
|
|
370
|
+
await logger.startAsync('Detect voice activity with Whisper model');
|
|
371
|
+
const sotToken = this.tokenConfig.startOfTextToken;
|
|
372
|
+
const initialTokens = [sotToken];
|
|
373
|
+
const offset = 0;
|
|
374
|
+
const Onnx = await import('onnxruntime-node');
|
|
375
|
+
const initialKvDimensions = this.getKvDimensions(1, initialTokens.length);
|
|
376
|
+
const kvCacheTensor = new Onnx.Tensor('float32', new Float32Array(initialKvDimensions[0] * initialKvDimensions[1] * initialKvDimensions[2] * initialKvDimensions[3]), initialKvDimensions);
|
|
377
|
+
const tokensTensor = new Onnx.Tensor('int64', new BigInt64Array(initialTokens.map(token => BigInt(token))), [1, initialTokens.length]);
|
|
378
|
+
const offsetTensor = new Onnx.Tensor('int64', new BigInt64Array([BigInt(offset)]), []);
|
|
379
|
+
const decoderInputs = {
|
|
380
|
+
tokens: tokensTensor,
|
|
381
|
+
audio_features: audioFeatures,
|
|
382
|
+
kv_cache: kvCacheTensor,
|
|
383
|
+
offset: offsetTensor
|
|
384
|
+
};
|
|
385
|
+
const decoderOutputs = await this.textDecoder.run(decoderInputs);
|
|
386
|
+
const logitsBuffer = decoderOutputs['logits'].data;
|
|
387
|
+
const tokenConfig = this.tokenConfig;
|
|
388
|
+
const logits = Array.from(logitsBuffer);
|
|
389
|
+
const probabilities = softmax(logits, temperature);
|
|
390
|
+
const noSpeechProbability = probabilities[tokenConfig.nonSpeechToken];
|
|
391
|
+
return 1.0 - noSpeechProbability;
|
|
392
|
+
}
|
|
393
|
+
// Decode tokens using the decoder model
|
|
394
|
+
async decodeTokens(audioFeatures, initialTokens, audioDuration, isFirstPart, isFinalPart, options, logitFilter) {
|
|
395
|
+
// Initialize
|
|
321
396
|
await this.initializeTokenizerIfNeeded();
|
|
322
397
|
await this.initializeDecoderSessionIfNeeded();
|
|
323
398
|
const logger = new Logger();
|
|
324
|
-
const allowedPunctuationMarks = this.getAllowedPunctuationMarks();
|
|
325
399
|
await logger.startAsync('Decode text tokens with Whisper decoder model');
|
|
326
400
|
options = extendDeep(defaultWhisperOptions, options);
|
|
327
401
|
const Onnx = await import('onnxruntime-node');
|
|
402
|
+
// Get token information
|
|
328
403
|
const endOfTextToken = this.tokenConfig.endOfTextToken;
|
|
329
404
|
const timestampTokensStart = this.tokenConfig.timestampTokensStart;
|
|
330
|
-
const
|
|
405
|
+
const suppressedTextTokens = this.getSuppressedTextTokens();
|
|
406
|
+
const suppressedMetadataTokens = this.getSuppressedMetadataTokens();
|
|
407
|
+
const allowedPunctuationMarks = this.getAllowedPunctuationMarks();
|
|
331
408
|
const spaceToken = this.textToTokens(' ')[0];
|
|
332
|
-
|
|
409
|
+
// Initialize variables for decoding loop
|
|
333
410
|
let decodedTokens = initialTokens.slice();
|
|
334
411
|
const initialKvDimensions = this.getKvDimensions(1, decodedTokens.length);
|
|
335
412
|
let kvCacheTensor = new Onnx.Tensor('float32', new Float32Array(initialKvDimensions[0] * initialKvDimensions[1] * initialKvDimensions[2] * initialKvDimensions[3]), initialKvDimensions);
|
|
336
|
-
let decodedTokensTimestampLogits = [
|
|
337
|
-
let
|
|
338
|
-
let timestampsSeenCount = 0;
|
|
339
|
-
const decodedTokensConfidence = [];
|
|
413
|
+
let decodedTokensTimestampLogits = [];
|
|
414
|
+
let decodedTokensConfidence = [];
|
|
340
415
|
let decodedTokensCrossAttentionQKs = [];
|
|
341
416
|
for (let i = 0; i < decodedTokens.length; i++) {
|
|
417
|
+
decodedTokensTimestampLogits.push(new Array(1501));
|
|
418
|
+
decodedTokensConfidence.push(1.0);
|
|
342
419
|
decodedTokensCrossAttentionQKs.push(undefined);
|
|
343
420
|
}
|
|
421
|
+
let lastTimestampTokenIndex = -1;
|
|
422
|
+
let timestampTokenSeenCount = 0;
|
|
344
423
|
let bufferedTokensToPrint = [];
|
|
424
|
+
// Define method to add a token to output
|
|
425
|
+
function addToken(tokenToAdd, timestampLogits, confidence, crossAttentionQKs) {
|
|
426
|
+
decodedTokens.push(tokenToAdd);
|
|
427
|
+
decodedTokensTimestampLogits.push(timestampLogits);
|
|
428
|
+
decodedTokensConfidence.push(confidence);
|
|
429
|
+
decodedTokensCrossAttentionQKs.push(crossAttentionQKs);
|
|
430
|
+
}
|
|
345
431
|
// Start decoding loop
|
|
346
|
-
for (let decodedTokenCount = 0; decodedTokenCount <
|
|
432
|
+
for (let decodedTokenCount = 0; decodedTokenCount < options.maxTokensPerPart; decodedTokenCount++) {
|
|
347
433
|
const isInitialState = decodedTokens.length == initialTokens.length;
|
|
348
|
-
|
|
349
|
-
const offset = isInitialState ? 0 : decodedTokens.length;
|
|
434
|
+
// If not in initial state, reshape KV Cache tensor to accomodate a new output token
|
|
350
435
|
if (!isInitialState) {
|
|
351
|
-
// Reshape KV Cache tensor
|
|
352
436
|
const dims = kvCacheTensor.dims;
|
|
353
437
|
const currentKvCacheGroups = splitFloat32Array(kvCacheTensor.data, dims[2] * dims[3]);
|
|
354
438
|
const reshapedKvCacheTensor = new Onnx.Tensor('float32', new Float32Array(dims[0] * dims[1] * (decodedTokens.length) * dims[3]), [dims[0], dims[1], decodedTokens.length, dims[3]]);
|
|
@@ -358,199 +442,252 @@ export class Whisper {
|
|
|
358
442
|
}
|
|
359
443
|
kvCacheTensor = reshapedKvCacheTensor;
|
|
360
444
|
}
|
|
361
|
-
// Prepare
|
|
445
|
+
// Prepare values for decoder
|
|
446
|
+
const tokensToDecode = isInitialState ? decodedTokens : [decodedTokens[decodedTokens.length - 1]];
|
|
447
|
+
const offset = isInitialState ? 0 : decodedTokens.length;
|
|
362
448
|
const tokensTensor = new Onnx.Tensor('int64', new BigInt64Array(tokensToDecode.map(token => BigInt(token))), [1, tokensToDecode.length]);
|
|
363
449
|
const offsetTensor = new Onnx.Tensor('int64', new BigInt64Array([BigInt(offset)]), []);
|
|
364
|
-
const decoderInputs = {
|
|
450
|
+
const decoderInputs = {
|
|
451
|
+
tokens: tokensTensor,
|
|
452
|
+
audio_features: audioFeatures,
|
|
453
|
+
kv_cache: kvCacheTensor,
|
|
454
|
+
offset: offsetTensor
|
|
455
|
+
};
|
|
456
|
+
// Run decoder
|
|
365
457
|
const decoderOutputs = await this.textDecoder.run(decoderInputs);
|
|
458
|
+
// Store results
|
|
366
459
|
const logitsBuffer = decoderOutputs['logits'].data;
|
|
367
460
|
kvCacheTensor = decoderOutputs['output_kv_cache'];
|
|
368
|
-
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const shouldSuppressToken = isWrongTokenForInitialState || isInSuppressedList;
|
|
378
|
-
if (shouldSuppressToken) {
|
|
379
|
-
allTokenLogits[logitIndex] = -Infinity;
|
|
380
|
-
}
|
|
461
|
+
const crossAttentionQKsForTokenOnnx = decoderOutputs['cross_attention_qks'];
|
|
462
|
+
const crossAttentionQKsForToken = makeOnnxLikeFloat32Tensor(crossAttentionQKsForTokenOnnx);
|
|
463
|
+
crossAttentionQKsForTokenOnnx.dispose();
|
|
464
|
+
// Get logits
|
|
465
|
+
const resultLogitsFloatArrays = splitFloat32Array(logitsBuffer, logitsBuffer.length / decoderOutputs['logits'].dims[1]);
|
|
466
|
+
const allTokenLogits = Array.from(resultLogitsFloatArrays[resultLogitsFloatArrays.length - 1]);
|
|
467
|
+
// Suppress metadata tokens in the suppression set
|
|
468
|
+
for (const suppressedTokenIndex of suppressedMetadataTokens) {
|
|
469
|
+
allTokenLogits[suppressedTokenIndex] = -Infinity;
|
|
381
470
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
decodedTokensTimestampLogits.push(timestampLogits);
|
|
386
|
-
decodedTokensCrossAttentionQKs.push(decoderOutputs['cross_attention_qks']);
|
|
387
|
-
decodedTokensConfidence.push(confidence);
|
|
471
|
+
if (isInitialState) {
|
|
472
|
+
// If in initial state, suppress end-of-text token
|
|
473
|
+
allTokenLogits[endOfTextToken] = -Infinity;
|
|
388
474
|
}
|
|
389
|
-
|
|
390
|
-
|
|
475
|
+
const timestampTokenLogits = allTokenLogits.slice(timestampTokensStart);
|
|
476
|
+
const decodeTimestampTokenIfNeeded = () => {
|
|
477
|
+
// Try to decode a timestamp token, if needed
|
|
478
|
+
// If timestamp tokens is disabled in options, don't decode a timestamp
|
|
479
|
+
if (!options.decodeTimestampTokens) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
const previousTokenWasTimestamp = this.isTimestampToken(decodedTokens[decodedTokens.length - 1]);
|
|
483
|
+
const secondPreviousTokenWasTimestamp = this.isTimestampToken(decodedTokens[decodedTokens.length - 2]);
|
|
484
|
+
// If there are two successive timestamp tokens decoded, or the previous timestamp was the first token,
|
|
485
|
+
// don't decode a timestamp
|
|
486
|
+
if (previousTokenWasTimestamp &&
|
|
487
|
+
(decodedTokens.length === initialTokens.length + 1) || secondPreviousTokenWasTimestamp) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
391
490
|
// Derive token probabilities
|
|
392
491
|
const probabilities = softmax(allTokenLogits, 1.0);
|
|
393
492
|
const logProbabilities = logOfVector(probabilities);
|
|
394
493
|
const nonTimestampTokenLogProbs = logProbabilities.slice(0, timestampTokensStart);
|
|
494
|
+
// Find highest non-timestamp token
|
|
395
495
|
const indexOfMaxNonTimestampLogProb = indexOfMax(nonTimestampTokenLogProbs);
|
|
396
496
|
const valueOfMaxNonTimestampLogProb = nonTimestampTokenLogProbs[indexOfMaxNonTimestampLogProb];
|
|
497
|
+
// Find highest timestamp token
|
|
397
498
|
const timestampTokenLogProbs = logProbabilities.slice(timestampTokensStart);
|
|
398
499
|
const indexOfMaxTimestampLogProb = indexOfMax(timestampTokenLogProbs);
|
|
500
|
+
// Compute the log of the sum of exponentials of the log probabilities
|
|
501
|
+
// of the timestamp tokens
|
|
399
502
|
const logSumExpOfTimestampTokenLogProbs = logSumExp(timestampTokenLogProbs);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
timestampsSeenCount += 1;
|
|
503
|
+
// If the sum isn't greater than the log probability of the highest non-timestamp token,
|
|
504
|
+
// don't decode a timestamp
|
|
505
|
+
if (logSumExpOfTimestampTokenLogProbs <= valueOfMaxNonTimestampLogProb) {
|
|
506
|
+
return false;
|
|
405
507
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const timestampToken = timestampTokensStart + indexOfMaxTimestampLogProb;
|
|
420
|
-
const confidence = probabilities[timestampToken];
|
|
421
|
-
addToken(timestampToken, timestampTokenLogits, confidence);
|
|
422
|
-
}
|
|
423
|
-
shouldDecodeNonTimestampToken = false;
|
|
508
|
+
// Decode a timestamp token
|
|
509
|
+
timestampTokenSeenCount += 1;
|
|
510
|
+
if (previousTokenWasTimestamp) {
|
|
511
|
+
const previousToken = decodedTokens[decodedTokens.length - 1];
|
|
512
|
+
const previousTokenTimestampLogits = decodedTokensTimestampLogits[decodedTokensTimestampLogits.length - 1];
|
|
513
|
+
const previousTokenConfidence = decodedTokensConfidence[decodedTokensConfidence.length - 1];
|
|
514
|
+
addToken(previousToken, previousTokenTimestampLogits, previousTokenConfidence, crossAttentionQKsForToken);
|
|
515
|
+
lastTimestampTokenIndex = decodedTokens.length;
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
const timestampToken = timestampTokensStart + indexOfMaxTimestampLogProb;
|
|
519
|
+
const confidence = probabilities[timestampToken];
|
|
520
|
+
addToken(timestampToken, timestampTokenLogits, confidence, crossAttentionQKsForToken);
|
|
424
521
|
}
|
|
522
|
+
return true;
|
|
523
|
+
};
|
|
524
|
+
const timestampTokenDecoded = decodeTimestampTokenIfNeeded();
|
|
525
|
+
if (timestampTokenDecoded) {
|
|
526
|
+
await yieldToEventLoop();
|
|
527
|
+
continue;
|
|
425
528
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
if (options.
|
|
439
|
-
|
|
440
|
-
const lastDecodedTextTokens = decodedTokens.filter(token => this.isTextToken(token)).reverse().slice(0, 20);
|
|
441
|
-
const { maxScore } = getRepetitionScoreRelativeToFirstSubstring([entry.token, ...lastDecodedTextTokens]);
|
|
442
|
-
return maxScore;
|
|
443
|
-
});
|
|
444
|
-
const thresholdRepetitionScore = 4;
|
|
445
|
-
if (topCandidatesRepetitionScores.every(score => score >= thresholdRepetitionScore)) {
|
|
446
|
-
const indexOfMaxScore = topCandidatesRepetitionScores.indexOf(Math.max(...topCandidatesRepetitionScores));
|
|
447
|
-
topCandidates = [topCandidates[indexOfMaxScore]];
|
|
448
|
-
}
|
|
449
|
-
else {
|
|
450
|
-
topCandidates = topCandidates.filter((candidate, index) => topCandidatesRepetitionScores[index] < thresholdRepetitionScore);
|
|
451
|
-
}
|
|
529
|
+
// Decode a non-timestamp token
|
|
530
|
+
let nonTimestampTokenLogits = allTokenLogits.slice(0, timestampTokensStart);
|
|
531
|
+
let shouldDecodeEndfOfTextToken = false;
|
|
532
|
+
// If not in initial state, and the end-of-text token's probability is sufficiently higher than
|
|
533
|
+
// the second highest ranked token, then set to accept it
|
|
534
|
+
if (!isInitialState) {
|
|
535
|
+
const endOfTextTokenLogit = nonTimestampTokenLogits[endOfTextToken];
|
|
536
|
+
const otherTokensLogits = nonTimestampTokenLogits.slice();
|
|
537
|
+
otherTokensLogits[endOfTextToken] = -Infinity;
|
|
538
|
+
const indexOfMaximumOtherTokenLogit = indexOfMax(otherTokensLogits);
|
|
539
|
+
const maximumOtherTokenLogit = nonTimestampTokenLogits[indexOfMaximumOtherTokenLogit];
|
|
540
|
+
const endProbabilities = softmax([endOfTextTokenLogit, maximumOtherTokenLogit], 1.0);
|
|
541
|
+
if (endProbabilities[0] > options.endTokenThreshold) {
|
|
542
|
+
shouldDecodeEndfOfTextToken = true;
|
|
452
543
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return true;
|
|
462
|
-
});
|
|
544
|
+
}
|
|
545
|
+
if (logitFilter) {
|
|
546
|
+
// Apply custom logit filter function if given
|
|
547
|
+
nonTimestampTokenLogits = logitFilter(nonTimestampTokenLogits, decodedTokens, isFirstPart, isFinalPart);
|
|
548
|
+
// If the custom filter set the end-of-text token to be Infinity, or -Infinity,
|
|
549
|
+
// then override any previous decision and accept or reject it, respectively
|
|
550
|
+
if (nonTimestampTokenLogits[endOfTextToken] === Infinity) {
|
|
551
|
+
shouldDecodeEndfOfTextToken = true;
|
|
463
552
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
const tokenText = this.tokenToText(entry.token).trim();
|
|
467
|
-
const isPunctuationToken = allowedPunctuationMarks.includes(tokenText);
|
|
468
|
-
if (!isPunctuationToken) {
|
|
469
|
-
return false;
|
|
470
|
-
}
|
|
471
|
-
const tokenProb = topCandidateProbabilities[index];
|
|
472
|
-
return tokenProb >= options.punctuationThreshold;
|
|
473
|
-
});
|
|
474
|
-
let rankOfSpaceToken = topCandidates.findIndex(candidate => candidate.token === spaceToken);
|
|
475
|
-
if (rankOfSpaceToken < 0) {
|
|
476
|
-
rankOfSpaceToken = Infinity;
|
|
553
|
+
else if (nonTimestampTokenLogits[endOfTextToken] === -Infinity) {
|
|
554
|
+
shouldDecodeEndfOfTextToken = false;
|
|
477
555
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
556
|
+
// If filter caused all word token logits to be -Infinity, then there is no
|
|
557
|
+
// other token to decode. Fall back to accept end-of-text
|
|
558
|
+
if (nonTimestampTokenLogits.slice(0, endOfTextToken).every(logit => logit === -Infinity)) {
|
|
559
|
+
shouldDecodeEndfOfTextToken = true;
|
|
482
560
|
}
|
|
483
|
-
|
|
484
|
-
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
// Otherwise, suppress text tokens in the suppression set
|
|
564
|
+
for (const suppressedTokenIndex of suppressedTextTokens) {
|
|
565
|
+
nonTimestampTokenLogits[suppressedTokenIndex] = -Infinity;
|
|
485
566
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (chosenToken === endOfTextToken) {
|
|
490
|
-
break;
|
|
567
|
+
// Suppress space token if at initial state
|
|
568
|
+
if (isInitialState) {
|
|
569
|
+
nonTimestampTokenLogits[spaceToken] = -Infinity;
|
|
491
570
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
571
|
+
}
|
|
572
|
+
// If end-of-text token should be decoded, then add it and break
|
|
573
|
+
// out of the loop
|
|
574
|
+
if (shouldDecodeEndfOfTextToken) {
|
|
575
|
+
addToken(endOfTextToken, timestampTokenLogits, 1.0, crossAttentionQKsForToken);
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
// Suppress end-of-text if it shouldn't be included in candidates
|
|
579
|
+
if (!options.includeEndTokenInCandidates) {
|
|
580
|
+
nonTimestampTokenLogits[endOfTextToken] = -Infinity;
|
|
581
|
+
}
|
|
582
|
+
// Find top candidates
|
|
583
|
+
const sortedNonTimestampLogitsWithIndexes = Array.from(nonTimestampTokenLogits).map((logit, index) => ({ token: index, logit }));
|
|
584
|
+
sortedNonTimestampLogitsWithIndexes.sort((a, b) => b.logit - a.logit);
|
|
585
|
+
let topCandidates = sortedNonTimestampLogitsWithIndexes.slice(0, options.topCandidateCount)
|
|
586
|
+
.map(entry => ({
|
|
587
|
+
token: entry.token,
|
|
588
|
+
logit: entry.logit,
|
|
589
|
+
text: this.tokenToText(entry.token, true)
|
|
590
|
+
}));
|
|
591
|
+
// Apply repetition suppression if enabled
|
|
592
|
+
if (options.suppressRepetition) {
|
|
593
|
+
// Using some hardcoded constants, for now
|
|
594
|
+
const tokenWindowSize = 30;
|
|
595
|
+
const thresholdMatchLength = 6;
|
|
596
|
+
const thresholdCycleRepetition = 2.0;
|
|
597
|
+
const filteredCandidates = [];
|
|
598
|
+
for (const candidate of topCandidates) {
|
|
599
|
+
const lastDecodedTextTokens = decodedTokens
|
|
600
|
+
.filter(token => this.isTextToken(token))
|
|
601
|
+
.reverse()
|
|
602
|
+
.slice(0, tokenWindowSize);
|
|
603
|
+
const { longestMatch, longestCycleRepetition } = getTokenRepetitionScore([candidate.token, ...lastDecodedTextTokens]);
|
|
604
|
+
if (longestMatch >= thresholdMatchLength || longestCycleRepetition >= thresholdCycleRepetition) {
|
|
605
|
+
continue;
|
|
501
606
|
}
|
|
607
|
+
filteredCandidates.push(candidate);
|
|
608
|
+
}
|
|
609
|
+
// If all candidates have been filtered out, accept an end-of-text token
|
|
610
|
+
if (filteredCandidates.length === 0) {
|
|
611
|
+
filteredCandidates.push({
|
|
612
|
+
token: endOfTextToken,
|
|
613
|
+
logit: Infinity,
|
|
614
|
+
text: this.tokenToText(endOfTextToken, true)
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
topCandidates = filteredCandidates;
|
|
618
|
+
}
|
|
619
|
+
// Compute top candidate probabilities
|
|
620
|
+
const topCandidateProbabilities = softmax(topCandidates.map(a => a.logit), options.temperature);
|
|
621
|
+
// Find highest ranking punctuation token
|
|
622
|
+
const rankOfPromisingPunctuationToken = topCandidates.findIndex((entry, index) => {
|
|
623
|
+
const tokenText = this.tokenToText(entry.token).trim();
|
|
624
|
+
const isPunctuationToken = allowedPunctuationMarks.includes(tokenText);
|
|
625
|
+
if (!isPunctuationToken) {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
const tokenProb = topCandidateProbabilities[index];
|
|
629
|
+
return tokenProb >= options.punctuationThreshold;
|
|
630
|
+
});
|
|
631
|
+
// Find rank of space token
|
|
632
|
+
let rankOfSpaceToken = topCandidates.findIndex(candidate => candidate.token === spaceToken);
|
|
633
|
+
if (rankOfSpaceToken < 0) {
|
|
634
|
+
rankOfSpaceToken = Infinity;
|
|
635
|
+
}
|
|
636
|
+
// Choose token
|
|
637
|
+
let chosenCandidateRank;
|
|
638
|
+
// Select a high-ranking punctuation token if found, and it has
|
|
639
|
+
// a rank higher than the space token,
|
|
640
|
+
if (rankOfPromisingPunctuationToken >= 0 &&
|
|
641
|
+
rankOfPromisingPunctuationToken < rankOfSpaceToken) {
|
|
642
|
+
chosenCandidateRank = rankOfPromisingPunctuationToken;
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
// Otherwise, select randomly from top k distribution
|
|
646
|
+
chosenCandidateRank = this.randomGen.selectRandomIndexFromDistribution(topCandidateProbabilities);
|
|
647
|
+
}
|
|
648
|
+
// Add chosen token
|
|
649
|
+
const chosenToken = topCandidates[chosenCandidateRank].token;
|
|
650
|
+
const chosenTokenConfidence = topCandidateProbabilities[chosenCandidateRank];
|
|
651
|
+
addToken(chosenToken, timestampTokenLogits, chosenTokenConfidence, crossAttentionQKsForToken);
|
|
652
|
+
// If chosen token is the end-of-text token, break
|
|
653
|
+
if (chosenToken === endOfTextToken) {
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
// Print token if needed
|
|
657
|
+
if (this.isTextToken(chosenToken)) {
|
|
658
|
+
bufferedTokensToPrint.push(chosenToken);
|
|
659
|
+
let textToPrint = this.tokensToText(bufferedTokensToPrint);
|
|
660
|
+
// If the decoded text is valid, print it
|
|
661
|
+
if (!containsInvalidCodepoint(textToPrint)) {
|
|
662
|
+
if (isFirstPart && decodedTokens.every(token => this.isMetadataToken(token))) {
|
|
663
|
+
textToPrint = textToPrint.trimStart();
|
|
664
|
+
}
|
|
665
|
+
logger.write(textToPrint);
|
|
666
|
+
bufferedTokensToPrint = [];
|
|
502
667
|
}
|
|
503
668
|
}
|
|
504
669
|
await yieldToEventLoop();
|
|
505
670
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
671
|
+
// If at least two timestamp tokens were decoded and it's not the final part,
|
|
672
|
+
// truncate up to the last timestamp token
|
|
673
|
+
if (timestampTokenSeenCount >= 2 && !isFinalPart) {
|
|
674
|
+
const sliceEndTokenIndex = lastTimestampTokenIndex;
|
|
675
|
+
decodedTokens = decodedTokens.slice(0, sliceEndTokenIndex);
|
|
676
|
+
decodedTokensTimestampLogits = decodedTokensTimestampLogits.slice(0, sliceEndTokenIndex);
|
|
677
|
+
decodedTokensCrossAttentionQKs = decodedTokensCrossAttentionQKs.slice(0, sliceEndTokenIndex);
|
|
678
|
+
decodedTokensConfidence = decodedTokensConfidence.slice(0, sliceEndTokenIndex);
|
|
510
679
|
}
|
|
511
680
|
logger.write('\n');
|
|
512
681
|
logger.end();
|
|
513
|
-
// Return the tokens
|
|
682
|
+
// Return the decoded tokens
|
|
514
683
|
return {
|
|
515
684
|
decodedTokens,
|
|
516
685
|
decodedTokensTimestampLogits,
|
|
517
|
-
|
|
518
|
-
|
|
686
|
+
decodedTokensConfidence,
|
|
687
|
+
decodedTokensCrossAttentionQKs,
|
|
519
688
|
};
|
|
520
689
|
}
|
|
521
|
-
|
|
522
|
-
const offset = 0;
|
|
523
|
-
const Onnx = await import('onnxruntime-node');
|
|
524
|
-
const tokensTensor = new Onnx.Tensor('int64', new BigInt64Array(tokens.map(token => BigInt(token))), [1, tokens.length]);
|
|
525
|
-
const offsetTensor = new Onnx.Tensor('int64', new BigInt64Array([BigInt(offset)]), []);
|
|
526
|
-
const initialKvDimensions = this.getKvDimensions(1, tokens.length);
|
|
527
|
-
const kvCacheTensor = new Onnx.Tensor('float32', new Float32Array(initialKvDimensions[0] * initialKvDimensions[1] * initialKvDimensions[2] * initialKvDimensions[3]), initialKvDimensions);
|
|
528
|
-
const decoderInputs = { tokens: tokensTensor, audio_features: audioFeatures, kv_cache: kvCacheTensor, offset: offsetTensor };
|
|
529
|
-
const decoderOutputs = await this.textDecoder.run(decoderInputs);
|
|
530
|
-
const crossAttentionQKsTensor = decoderOutputs['cross_attention_qks'];
|
|
531
|
-
const tensorShape = crossAttentionQKsTensor.dims.slice();
|
|
532
|
-
const ndarray = (await import('ndarray')).default;
|
|
533
|
-
let qkArray = ndarray(crossAttentionQKsTensor.data, crossAttentionQKsTensor.dims.slice());
|
|
534
|
-
qkArray = qkArray.transpose(3, 0, 1, 2, 4);
|
|
535
|
-
const tokenCrossAttentionQKsTensors = [];
|
|
536
|
-
for (let i0 = 0; i0 < qkArray.shape[0]; i0++) {
|
|
537
|
-
const dataForToken = [];
|
|
538
|
-
for (let i1 = 0; i1 < qkArray.shape[1]; i1++) {
|
|
539
|
-
for (let i2 = 0; i2 < qkArray.shape[2]; i2++) {
|
|
540
|
-
for (let i3 = 0; i3 < qkArray.shape[3]; i3++) {
|
|
541
|
-
for (let i4 = 0; i4 < qkArray.shape[4]; i4++) {
|
|
542
|
-
dataForToken.push(qkArray.get(i0, i1, i2, i3, i4));
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
const newTensorShape = tensorShape.slice();
|
|
548
|
-
newTensorShape[3] = 1;
|
|
549
|
-
const newTensor = new Onnx.Tensor('float32', dataForToken, newTensorShape);
|
|
550
|
-
tokenCrossAttentionQKsTensors.push(newTensor);
|
|
551
|
-
}
|
|
552
|
-
return tokenCrossAttentionQKsTensors;
|
|
553
|
-
}
|
|
690
|
+
// Encode audio using the encoder model
|
|
554
691
|
async encodeAudio(rawAudio) {
|
|
555
692
|
await this.initializeEncoderSessionIfNeeded();
|
|
556
693
|
const Onnx = await import('onnxruntime-node');
|
|
@@ -562,9 +699,12 @@ export class Whisper {
|
|
|
562
699
|
const filterbankCount = 80;
|
|
563
700
|
const maxAudioSamples = sampleRate * 30;
|
|
564
701
|
const maxAudioFrames = 3000;
|
|
702
|
+
if (audioSamples.length > maxAudioSamples) {
|
|
703
|
+
throw new Error(`Audio part is longer than 30 seconds`);
|
|
704
|
+
}
|
|
565
705
|
await logger.startAsync('Extract mel spectogram from audio part');
|
|
566
706
|
const paddedAudioSamples = new Float32Array(maxAudioSamples);
|
|
567
|
-
paddedAudioSamples.set(audioSamples
|
|
707
|
+
paddedAudioSamples.set(audioSamples, 0);
|
|
568
708
|
const rawAudioPart = { audioChannels: [paddedAudioSamples], sampleRate };
|
|
569
709
|
const { melSpectogram } = await computeMelSpectogramUsingFilterbanks(rawAudioPart, fftOrder, fftOrder, hopLength, filterbanks);
|
|
570
710
|
await logger.startAsync('Normalize mel spectogram');
|
|
@@ -592,99 +732,6 @@ export class Whisper {
|
|
|
592
732
|
logger.end();
|
|
593
733
|
return encodedAudioFeatures;
|
|
594
734
|
}
|
|
595
|
-
addSegmentsToTimeline(timeline, tokens, initialTimeOffset, audioDuration) {
|
|
596
|
-
const timestampTokensStart = this.tokenConfig.timestampTokensStart;
|
|
597
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
598
|
-
const token = tokens[i];
|
|
599
|
-
if (token == this.tokenConfig.startOfTextToken || token == this.tokenConfig.endOfTextToken) {
|
|
600
|
-
continue;
|
|
601
|
-
}
|
|
602
|
-
const tokenIsTimestamp = token >= timestampTokensStart;
|
|
603
|
-
const previousTokenWasTimestamp = tokens.length > 1 && tokens[i - 1] >= timestampTokensStart;
|
|
604
|
-
if (tokenIsTimestamp) {
|
|
605
|
-
if (previousTokenWasTimestamp) {
|
|
606
|
-
continue;
|
|
607
|
-
}
|
|
608
|
-
let startTime = initialTimeOffset + this.timestampTokenToSeconds(token);
|
|
609
|
-
startTime = Math.min(startTime, audioDuration);
|
|
610
|
-
if (timeline.length > 0) {
|
|
611
|
-
timeline[timeline.length - 1].endTime = startTime;
|
|
612
|
-
}
|
|
613
|
-
timeline.push({
|
|
614
|
-
type: 'segment',
|
|
615
|
-
text: '',
|
|
616
|
-
startTime,
|
|
617
|
-
endTime: -1,
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
else {
|
|
621
|
-
if (timeline.length == 0) {
|
|
622
|
-
timeline.push({
|
|
623
|
-
type: 'segment',
|
|
624
|
-
text: '',
|
|
625
|
-
startTime: initialTimeOffset,
|
|
626
|
-
endTime: -1,
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
const tokenText = this.tokenToText(token);
|
|
630
|
-
timeline[timeline.length - 1].text += tokenText;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
async addWordsToTimeline(timeline, tokens, rawAudio, crossAttentionQKs, initialAudioTimeOffset, duration) {
|
|
635
|
-
let segmentStartTime = 0;
|
|
636
|
-
let segmentTokens = [];
|
|
637
|
-
let segmentCrossAttentionQKs = [];
|
|
638
|
-
for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex++) {
|
|
639
|
-
const token = tokens[tokenIndex];
|
|
640
|
-
const tokenCrossAttentionQKs = crossAttentionQKs[tokenIndex];
|
|
641
|
-
const segmentTokensWithoutTimestamps = segmentTokens.filter(token => this.isNonTimestampToken(token));
|
|
642
|
-
const isTimestamp = this.isTimestampToken(token);
|
|
643
|
-
if (isTimestamp || tokenIndex == tokens.length - 1) {
|
|
644
|
-
let tokenTime;
|
|
645
|
-
if (isTimestamp) {
|
|
646
|
-
tokenTime = this.timestampTokenToSeconds(token);
|
|
647
|
-
}
|
|
648
|
-
else {
|
|
649
|
-
tokenTime = duration;
|
|
650
|
-
}
|
|
651
|
-
if (segmentTokensWithoutTimestamps.length > 0) {
|
|
652
|
-
const segmentEndTime = tokenTime;
|
|
653
|
-
const segmentStartFrame = this.secondsToFrame(segmentStartTime);
|
|
654
|
-
let segmentEndFrame = this.secondsToFrame(segmentEndTime);
|
|
655
|
-
if (segmentStartFrame == segmentEndFrame) {
|
|
656
|
-
segmentEndFrame += 1;
|
|
657
|
-
}
|
|
658
|
-
const segmentFrameCount = segmentEndFrame - segmentStartFrame;
|
|
659
|
-
const reinferCrossAttentionQKs = true;
|
|
660
|
-
if (reinferCrossAttentionQKs) {
|
|
661
|
-
const initialTokens = this.getTextStartTokens('en', 'transcribe');
|
|
662
|
-
const tokensToDecode = [...initialTokens, ...segmentTokensWithoutTimestamps];
|
|
663
|
-
//const segmentAudioFeaturesBuffer = audioFeatures.data.slice(segmentStartFrame * audioFeatures.dims[2], segmentEndFrame * audioFeatures.dims[2])
|
|
664
|
-
//const segmentAudioFeatures = new Onnx.Tensor('float32', segmentAudioFeaturesBuffer, [1, segmentFrameCount, audioFeatures.dims[2]])
|
|
665
|
-
const segmentAudioSamples = rawAudio.audioChannels[0].slice(Math.floor(segmentStartTime * rawAudio.sampleRate), Math.floor(segmentEndTime * rawAudio.sampleRate));
|
|
666
|
-
const segmentRawAudio = { audioChannels: [segmentAudioSamples], sampleRate: rawAudio.sampleRate };
|
|
667
|
-
const segmentAudioFeatures = await this.encodeAudio(segmentRawAudio);
|
|
668
|
-
const reinferredCrossAttentionQKs = await this.inferCrossAttentionQKs(tokensToDecode, segmentAudioFeatures);
|
|
669
|
-
reinferredCrossAttentionQKs.slice(initialTokens.length);
|
|
670
|
-
const alignmentPath = await this.findAlignmentPathFromQKs(reinferredCrossAttentionQKs, tokensToDecode, 0, segmentFrameCount); //, alignmentHeadsIndexes[modelName])
|
|
671
|
-
const tokenTimeline = await this.getTokenTimelineFromAlignmentPath(alignmentPath, segmentTokensWithoutTimestamps, initialAudioTimeOffset + segmentStartTime, initialAudioTimeOffset + segmentEndTime);
|
|
672
|
-
timeline.push(...tokenTimeline);
|
|
673
|
-
}
|
|
674
|
-
else {
|
|
675
|
-
const alignmentPath = await this.findAlignmentPathFromQKs(segmentCrossAttentionQKs, segmentTokens, segmentStartFrame, segmentEndFrame); //, alignmentHeadsIndexes[modelName])
|
|
676
|
-
const tokenTimeline = await this.getTokenTimelineFromAlignmentPath(alignmentPath, segmentTokens, initialAudioTimeOffset, initialAudioTimeOffset + segmentEndTime);
|
|
677
|
-
timeline.push(...tokenTimeline);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
segmentStartTime = tokenTime;
|
|
681
|
-
segmentTokens = [];
|
|
682
|
-
segmentCrossAttentionQKs = [];
|
|
683
|
-
}
|
|
684
|
-
segmentTokens.push(token);
|
|
685
|
-
segmentCrossAttentionQKs.push(tokenCrossAttentionQKs);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
735
|
tokenTimelineToWordTimeline(tokenTimeline, language) {
|
|
689
736
|
function isSeparatorCharacter(char) {
|
|
690
737
|
const nonSeparatingPunctuation = [`'`, `-`, `.`, `·`, `•`];
|
|
@@ -699,6 +746,9 @@ export class Whisper {
|
|
|
699
746
|
function endsWithSeparatorCharacter(text) {
|
|
700
747
|
return isSeparatorCharacter(text[text.length - 1]);
|
|
701
748
|
}
|
|
749
|
+
if (language != 'zh' && language != 'ja') {
|
|
750
|
+
tokenTimeline = tokenTimeline.filter(entry => this.isTextToken(entry.id));
|
|
751
|
+
}
|
|
702
752
|
const resultTimeline = [];
|
|
703
753
|
let groups = [];
|
|
704
754
|
for (let tokenIndex = 0; tokenIndex < tokenTimeline.length; tokenIndex++) {
|
|
@@ -821,35 +871,35 @@ export class Whisper {
|
|
|
821
871
|
const applySoftmax = true;
|
|
822
872
|
const normalize = true;
|
|
823
873
|
const applyMedianFilter = true;
|
|
824
|
-
const
|
|
874
|
+
const anchorTimestampTokens = false;
|
|
825
875
|
const softmaxTemperature = 1.0;
|
|
826
|
-
|
|
876
|
+
// Apply softmax to each token's frames, if enabled
|
|
827
877
|
if (applySoftmax) {
|
|
828
|
-
// Apply softmax to each token's frames
|
|
829
878
|
for (const head of attentionHeads) {
|
|
830
879
|
for (let tokenIndex = 0; tokenIndex < tokenCount; tokenIndex++) {
|
|
831
880
|
head[tokenIndex] = softmax(head[tokenIndex], softmaxTemperature);
|
|
832
881
|
}
|
|
833
882
|
}
|
|
834
883
|
}
|
|
884
|
+
// Normalize all weights in each individual head, if enabled
|
|
835
885
|
if (normalize) {
|
|
836
|
-
// Normalize all weights in each individual head
|
|
837
886
|
for (const head of attentionHeads) {
|
|
838
887
|
const allWeightsForHead = head.flatMap(tokenFrames => tokenFrames);
|
|
839
|
-
const
|
|
840
|
-
const
|
|
888
|
+
const meanOfAllWeightsForHead = meanOfVector(allWeightsForHead);
|
|
889
|
+
const stdDeviationOfAllWeightsForHead = stdDeviationOfVector(allWeightsForHead, 'population', meanOfAllWeightsForHead) + 1e-10;
|
|
890
|
+
const stdDeviationReciprocal = 1.0 / (stdDeviationOfAllWeightsForHead + 1e-10);
|
|
841
891
|
for (const tokenFrames of head) {
|
|
842
892
|
for (let frameIndex = 0; frameIndex < tokenFrames.length; frameIndex++) {
|
|
843
|
-
tokenFrames[frameIndex] = (tokenFrames[frameIndex] -
|
|
893
|
+
tokenFrames[frameIndex] = (tokenFrames[frameIndex] - meanOfAllWeightsForHead) * stdDeviationReciprocal;
|
|
844
894
|
}
|
|
845
895
|
}
|
|
846
896
|
}
|
|
847
897
|
}
|
|
898
|
+
// Apply median filter to each token's frames, if enabled
|
|
848
899
|
if (applyMedianFilter) {
|
|
849
|
-
// Apply median filter to each token's frames
|
|
850
900
|
for (const head of attentionHeads) {
|
|
851
901
|
for (let tokenIndex = 0; tokenIndex < tokenCount; tokenIndex++) {
|
|
852
|
-
head[tokenIndex] =
|
|
902
|
+
head[tokenIndex] = medianOf5Filter(head[tokenIndex]);
|
|
853
903
|
}
|
|
854
904
|
}
|
|
855
905
|
}
|
|
@@ -869,8 +919,8 @@ export class Whisper {
|
|
|
869
919
|
frameMeansForToken[tokenIndex][frameIndex] = frameMean;
|
|
870
920
|
}
|
|
871
921
|
}
|
|
872
|
-
if
|
|
873
|
-
|
|
922
|
+
// Anchor timestamp tokens timestamps to their original values, if enabled
|
|
923
|
+
if (anchorTimestampTokens) {
|
|
874
924
|
const timestampTokensStart = this.tokenConfig.timestampTokensStart;
|
|
875
925
|
for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex++) {
|
|
876
926
|
const token = tokens[tokenIndex];
|
|
@@ -882,14 +932,86 @@ export class Whisper {
|
|
|
882
932
|
}
|
|
883
933
|
}
|
|
884
934
|
// Perform DTW
|
|
885
|
-
const tokenIndexes =
|
|
886
|
-
const frameIndexes =
|
|
935
|
+
const tokenIndexes = getIntegerRange(0, tokenCount);
|
|
936
|
+
const frameIndexes = getIntegerRange(0, segmentFrameCount);
|
|
887
937
|
let { path } = alignDTWWindowed(tokenIndexes, frameIndexes, (tokenIndex, frameIndex) => {
|
|
888
938
|
return -frameMeansForToken[tokenIndex][frameIndex];
|
|
889
939
|
}, segmentFrameCount);
|
|
890
940
|
path = path.map(entry => ({ source: entry.source, dest: segmentStartFrame + entry.dest }));
|
|
891
941
|
return path;
|
|
892
942
|
}
|
|
943
|
+
async initializeIfNeeded() {
|
|
944
|
+
await this.initializeTokenizerIfNeeded();
|
|
945
|
+
await this.initializeEncoderSessionIfNeeded();
|
|
946
|
+
await this.initializeDecoderSessionIfNeeded();
|
|
947
|
+
}
|
|
948
|
+
async initializeTokenizerIfNeeded() {
|
|
949
|
+
if (this.tiktoken) {
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
const logger = new Logger();
|
|
953
|
+
await logger.startAsync('Load tokenizer data');
|
|
954
|
+
const tiktokenModulePackagePath = await loadPackage('whisper-tiktoken-data');
|
|
955
|
+
const tiktokenDataFilePath = path.join(tiktokenModulePackagePath, this.isMultiligualModel ? 'multilingual.tiktoken' : 'gpt2.tiktoken');
|
|
956
|
+
let tiktokenData = await readFile(tiktokenDataFilePath, { encoding: 'utf8' });
|
|
957
|
+
const tokenConfig = this.tokenConfig;
|
|
958
|
+
const metadataTokens = {
|
|
959
|
+
[tokenConfig.endOfTextToken]: '[EndOfText]',
|
|
960
|
+
[tokenConfig.startOfTextToken]: '[StartOfText]',
|
|
961
|
+
[tokenConfig.translateTaskToken]: '[TranslateTask]',
|
|
962
|
+
[tokenConfig.transcribeTaskToken]: '[TranscribeTask]',
|
|
963
|
+
[tokenConfig.startOfPromptToken]: '[StartOfPrompt]',
|
|
964
|
+
[tokenConfig.nonSpeechToken]: '[NonSpeech]',
|
|
965
|
+
[tokenConfig.noTimestampsToken]: '[NoTimestamps]',
|
|
966
|
+
};
|
|
967
|
+
if (this.isMultiligualModel) {
|
|
968
|
+
metadataTokens[50256] = '[Unused_50256]';
|
|
969
|
+
metadataTokens[50360] = '[Unused_50360]';
|
|
970
|
+
}
|
|
971
|
+
const languageTokenCount = tokenConfig.languageTokensEnd - tokenConfig.languageTokensStart;
|
|
972
|
+
for (let i = 0; i < languageTokenCount; i++) {
|
|
973
|
+
const tokenIndex = this.tokenConfig.languageTokensStart + i;
|
|
974
|
+
metadataTokens[tokenIndex] = `[Language_${i}]`;
|
|
975
|
+
}
|
|
976
|
+
const timestampTokensCount = 1501;
|
|
977
|
+
for (let i = 0; i < timestampTokensCount; i++) {
|
|
978
|
+
const tokenIndex = this.tokenConfig.timestampTokensStart + i;
|
|
979
|
+
const tokenTime = this.timestampTokenToSeconds(tokenIndex);
|
|
980
|
+
metadataTokens[tokenIndex] = `[Timestamp_${tokenTime.toFixed(2)}]`;
|
|
981
|
+
}
|
|
982
|
+
const inverseMetadataTokensLookup = {};
|
|
983
|
+
for (const [key, value] of Object.entries(metadataTokens)) {
|
|
984
|
+
inverseMetadataTokensLookup[value] = parseInt(key);
|
|
985
|
+
}
|
|
986
|
+
const patternString = `'s|'t|'re|'ve|'m|'ll|'d| ?\\p{L}+| ?\\p{N}+| ?[^\\s\\p{L}\\p{N}]+|\\s+(?!\\S)|\\s+`;
|
|
987
|
+
const { Tiktoken } = await import('tiktoken/lite');
|
|
988
|
+
this.tiktoken = new Tiktoken(tiktokenData, inverseMetadataTokensLookup, patternString);
|
|
989
|
+
logger.end();
|
|
990
|
+
}
|
|
991
|
+
async initializeEncoderSessionIfNeeded() {
|
|
992
|
+
if (this.audioEncoder) {
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
const logger = new Logger();
|
|
996
|
+
await logger.startAsync(`Create encoder inference session for model '${this.modelName}'`);
|
|
997
|
+
const encoderFilePath = path.join(this.modelDir, 'encoder.onnx');
|
|
998
|
+
const Onnx = await import('onnxruntime-node');
|
|
999
|
+
const onnxSessionOptions = getOnnxSessionOptions({ executionProviders: this.encoderExecutionProviders });
|
|
1000
|
+
this.audioEncoder = await Onnx.InferenceSession.create(encoderFilePath, onnxSessionOptions);
|
|
1001
|
+
logger.end();
|
|
1002
|
+
}
|
|
1003
|
+
async initializeDecoderSessionIfNeeded() {
|
|
1004
|
+
if (this.textDecoder) {
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
const logger = new Logger();
|
|
1008
|
+
await logger.startAsync(`Create decoder inference session for model '${this.modelName}'`);
|
|
1009
|
+
const decoderFilePath = path.join(this.modelDir, 'decoder.onnx');
|
|
1010
|
+
const Onnx = await import('onnxruntime-node');
|
|
1011
|
+
const onnxSessionOptions = getOnnxSessionOptions({ executionProviders: this.decoderExecutionProviders });
|
|
1012
|
+
this.textDecoder = await Onnx.InferenceSession.create(decoderFilePath, onnxSessionOptions);
|
|
1013
|
+
logger.end();
|
|
1014
|
+
}
|
|
893
1015
|
getKvDimensions(groupCount, length) {
|
|
894
1016
|
const modelName = this.modelName;
|
|
895
1017
|
if (modelName == 'tiny' || modelName == 'tiny.en') {
|
|
@@ -1010,7 +1132,7 @@ export class Whisper {
|
|
|
1010
1132
|
}
|
|
1011
1133
|
getSuppressedTextTokens() {
|
|
1012
1134
|
const allowedPunctuationMarks = this.getAllowedPunctuationMarks();
|
|
1013
|
-
const nonWordTokensData = this.
|
|
1135
|
+
const nonWordTokensData = this.getWordTokenData().nonWordTokenData;
|
|
1014
1136
|
const suppressedTextTokens = nonWordTokensData
|
|
1015
1137
|
.filter(entry => !allowedPunctuationMarks.includes(entry.text))
|
|
1016
1138
|
.map(entry => entry.id);
|
|
@@ -1039,22 +1161,27 @@ export class Whisper {
|
|
|
1039
1161
|
}
|
|
1040
1162
|
return allowedPunctuation;
|
|
1041
1163
|
}
|
|
1042
|
-
|
|
1164
|
+
getWordTokenData() {
|
|
1165
|
+
const wordTokenData = [];
|
|
1043
1166
|
const nonWordTokenData = [];
|
|
1044
|
-
const invalidUTF8Char = String.fromCharCode(65533);
|
|
1045
1167
|
for (let i = 0; i < this.tokenConfig.endOfTextToken; i++) {
|
|
1046
1168
|
const tokenText = this.tokenToText(i, false);
|
|
1047
|
-
const
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
if (isNonWordToken && !containsInvalidUTF8) {
|
|
1169
|
+
const isNonWordToken = /^[\s\p{Punctuation}\p{Symbol}]+$/u.test(tokenText);
|
|
1170
|
+
const containsInvalidUTF8 = containsInvalidCodepoint(tokenText);
|
|
1171
|
+
if (isNonWordToken && (this.isEnglishOnlyModel || !containsInvalidUTF8)) {
|
|
1051
1172
|
nonWordTokenData.push({
|
|
1052
1173
|
id: i,
|
|
1053
1174
|
text: tokenText,
|
|
1054
1175
|
});
|
|
1055
1176
|
}
|
|
1177
|
+
else {
|
|
1178
|
+
wordTokenData.push({
|
|
1179
|
+
id: i,
|
|
1180
|
+
text: tokenText,
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1056
1183
|
}
|
|
1057
|
-
return nonWordTokenData;
|
|
1184
|
+
return { wordTokenData, nonWordTokenData };
|
|
1058
1185
|
}
|
|
1059
1186
|
getTokensData(tokens) {
|
|
1060
1187
|
const tokensData = [];
|
|
@@ -1067,88 +1194,6 @@ export class Whisper {
|
|
|
1067
1194
|
return tokensData;
|
|
1068
1195
|
}
|
|
1069
1196
|
}
|
|
1070
|
-
const filterbanks = [
|
|
1071
|
-
/* 0 */ { startIndex: 1, weights: [0.02486259490251541,] },
|
|
1072
|
-
/* 1 */ { startIndex: 1, weights: [0.001990821911022067, 0.022871771827340126,] },
|
|
1073
|
-
/* 2 */ { startIndex: 2, weights: [0.003981643822044134, 0.02088095061480999,] },
|
|
1074
|
-
/* 3 */ { startIndex: 3, weights: [0.0059724655002355576, 0.018890129402279854,] },
|
|
1075
|
-
/* 4 */ { startIndex: 4, weights: [0.007963287644088268, 0.01689930632710457,] },
|
|
1076
|
-
/* 5 */ { startIndex: 5, weights: [0.009954108856618404, 0.014908484183251858,] },
|
|
1077
|
-
/* 6 */ { startIndex: 6, weights: [0.011944931000471115, 0.012917662039399147,] },
|
|
1078
|
-
/* 7 */ { startIndex: 7, weights: [0.013935752213001251, 0.010926840826869011,] },
|
|
1079
|
-
/* 8 */ { startIndex: 8, weights: [0.015926575288176537, 0.0089360186830163,] },
|
|
1080
|
-
/* 9 */ { startIndex: 9, weights: [0.017917396500706673, 0.006945197004824877,] },
|
|
1081
|
-
/* 10 */ { startIndex: 10, weights: [0.01990821771323681, 0.004954374860972166,] },
|
|
1082
|
-
/* 11 */ { startIndex: 11, weights: [0.021899040788412094, 0.0029635531827807426,] },
|
|
1083
|
-
/* 12 */ { startIndex: 12, weights: [0.02388986200094223, 0.0009727313299663365,] },
|
|
1084
|
-
/* 13 */ { startIndex: 13, weights: [0.025880683213472366,] },
|
|
1085
|
-
/* 14 */ { startIndex: 14, weights: [0.025835324078798294,] },
|
|
1086
|
-
/* 15 */ { startIndex: 14, weights: [0.0010180906392633915, 0.023844502866268158,] },
|
|
1087
|
-
/* 16 */ { startIndex: 15, weights: [0.003008912317454815, 0.021853681653738022,] },
|
|
1088
|
-
/* 17 */ { startIndex: 16, weights: [0.004999734461307526, 0.019862858578562737,] },
|
|
1089
|
-
/* 18 */ { startIndex: 17, weights: [0.006990555673837662, 0.0178720373660326,] },
|
|
1090
|
-
/* 19 */ { startIndex: 18, weights: [0.008981377817690372, 0.015881216153502464,] },
|
|
1091
|
-
/* 20 */ { startIndex: 19, weights: [0.010972199961543083, 0.013890394009649754,] },
|
|
1092
|
-
/* 21 */ { startIndex: 20, weights: [0.01296302117407322, 0.011899571865797043,] },
|
|
1093
|
-
/* 22 */ { startIndex: 21, weights: [0.01495384331792593, 0.009908749721944332,] },
|
|
1094
|
-
/* 23 */ { startIndex: 22, weights: [0.01694466546177864, 0.007917927578091621,] },
|
|
1095
|
-
/* 24 */ { startIndex: 23, weights: [0.018935488536953926, 0.005927106365561485,] },
|
|
1096
|
-
/* 25 */ { startIndex: 24, weights: [0.020874010398983955, 0.004040425643324852,] },
|
|
1097
|
-
/* 26 */ { startIndex: 25, weights: [0.022114217281341553, 0.0033186059445142746,] },
|
|
1098
|
-
/* 27 */ { startIndex: 26, weights: [0.02173672430217266, 0.0036109676584601402,] },
|
|
1099
|
-
/* 28 */ { startIndex: 27, weights: [0.020497702062129974, 0.004762193653732538,] },
|
|
1100
|
-
/* 29 */ { startIndex: 28, weights: [0.018486659973859787, 0.006592618301510811,] },
|
|
1101
|
-
/* 30 */ { startIndex: 29, weights: [0.01585603691637516, 0.00896277092397213,] },
|
|
1102
|
-
/* 31 */ { startIndex: 30, weights: [0.012738768011331558, 0.011751330457627773,] },
|
|
1103
|
-
/* 32 */ { startIndex: 31, weights: [0.009250369854271412, 0.014853144995868206,] },
|
|
1104
|
-
/* 33 */ { startIndex: 32, weights: [0.005490840878337622, 0.018177473917603493, 0.0028155462350696325,] },
|
|
1105
|
-
/* 34 */ { startIndex: 33, weights: [0.0015463664894923568, 0.01632951945066452, 0.007420188747346401,] },
|
|
1106
|
-
/* 35 */ { startIndex: 35, weights: [0.011181050911545753, 0.012018864043056965,] },
|
|
1107
|
-
/* 36 */ { startIndex: 36, weights: [0.006065350491553545, 0.016561277210712433, 0.004360878840088844,] },
|
|
1108
|
-
/* 37 */ { startIndex: 37, weights: [0.0010297985281795263, 0.012770536355674267, 0.009707189165055752,] },
|
|
1109
|
-
/* 38 */ { startIndex: 39, weights: [0.006986402906477451, 0.01485429983586073, 0.004391219466924667,] },
|
|
1110
|
-
/* 39 */ { startIndex: 40, weights: [0.001418047584593296, 0.011486922390758991, 0.010089744813740253, 0.00040022286702878773,] },
|
|
1111
|
-
/* 40 */ { startIndex: 42, weights: [0.005411104764789343, 0.014735566452145576, 0.006518189795315266,] },
|
|
1112
|
-
/* 41 */ { startIndex: 44, weights: [0.00827841367572546, 0.012277561239898205, 0.00396781275048852,] },
|
|
1113
|
-
/* 42 */ { startIndex: 45, weights: [0.002187808509916067, 0.010184479877352715, 0.00998187530785799, 0.0022864851634949446,] },
|
|
1114
|
-
/* 43 */ { startIndex: 47, weights: [0.00386943481862545, 0.011274894699454308, 0.008466221392154694, 0.0013397691072896123,] },
|
|
1115
|
-
/* 44 */ { startIndex: 49, weights: [0.004820294212549925, 0.011678251437842846, 0.007608682848513126, 0.0010091039584949613,] },
|
|
1116
|
-
/* 45 */ { startIndex: 51, weights: [0.005156961735337973, 0.011507894843816757, 0.007301822770386934, 0.0011901655234396458,] },
|
|
1117
|
-
/* 46 */ { startIndex: 53, weights: [0.004982104524970055, 0.010863498784601688, 0.007451189681887627, 0.001791381393559277,] },
|
|
1118
|
-
/* 47 */ { startIndex: 55, weights: [0.004385921638458967, 0.009832492098212242, 0.007973956875503063, 0.002732589840888977,] },
|
|
1119
|
-
/* 48 */ { startIndex: 57, weights: [0.0034474546555429697, 0.008491347543895245, 0.008797688409686089, 0.00394382793456316,] },
|
|
1120
|
-
/* 49 */ { startIndex: 59, weights: [0.0022357646375894547, 0.0069067515432834625, 0.009859241545200348, 0.005364237818866968, 0.0008692338014952838,] },
|
|
1121
|
-
/* 50 */ { startIndex: 61, weights: [0.0008110002381727099, 0.005136650986969471, 0.00946230161935091, 0.0069410777650773525, 0.0027783995028585196,] },
|
|
1122
|
-
/* 51 */ { startIndex: 64, weights: [0.003231203882023692, 0.007237049750983715, 0.00862883497029543, 0.004773912951350212, 0.0009189908159896731,] },
|
|
1123
|
-
/* 52 */ { startIndex: 66, weights: [0.001233637798577547, 0.0049433219246566296, 0.008653006516397, 0.006818502210080624, 0.003248583758249879,] },
|
|
1124
|
-
/* 53 */ { startIndex: 69, weights: [0.0026164355222135782, 0.006051854696124792, 0.008880467154085636, 0.005574479699134827, 0.002268492942675948,] },
|
|
1125
|
-
/* 54 */ { startIndex: 71, weights: [0.0002863667905330658, 0.003467798000201583, 0.0066492292098701, 0.00787146482616663, 0.004809896927326918, 0.0017483289120718837,] },
|
|
1126
|
-
/* 55 */ { startIndex: 74, weights: [0.0009245910914614797, 0.0038708120118826628, 0.00681703258305788, 0.007283343467861414, 0.004448124207556248, 0.0016129047144204378,] },
|
|
1127
|
-
/* 56 */ { startIndex: 77, weights: [0.0011703289346769452, 0.003898728871718049, 0.006627128925174475, 0.0070473202504217625, 0.004421714693307877, 0.0017961094854399562,] },
|
|
1128
|
-
/* 57 */ { startIndex: 80, weights: [0.0010892992140725255, 0.003615982597693801, 0.006142666097730398, 0.007102936040610075, 0.004671447444707155, 0.002239959081634879,] },
|
|
1129
|
-
/* 58 */ { startIndex: 83, weights: [0.0007392280967906117, 0.0030791081953793764, 0.005418988410383463, 0.007397185545414686, 0.005145462695509195, 0.002893739379942417, 0.0006420162972062826,] },
|
|
1130
|
-
/* 59 */ { startIndex: 86, weights: [0.00017068670422304422, 0.0023375742603093386, 0.004504461772739887, 0.0066713495180010796, 0.005798479542136192, 0.003713231300935149, 0.0016279831761494279,] },
|
|
1131
|
-
/* 60 */ { startIndex: 90, weights: [0.0014345343224704266, 0.0034412189852446318, 0.005447904113680124, 0.006591092795133591, 0.004660011734813452, 0.002728930441662669, 0.0007978491485118866,] },
|
|
1132
|
-
/* 61 */ { startIndex: 93, weights: [0.0004075043834745884, 0.002265830524265766, 0.004124156199395657, 0.005982482805848122, 0.005700822453945875, 0.003912510350346565, 0.0021241982467472553, 0.0003358862304594368,] },
|
|
1133
|
-
/* 62 */ { startIndex: 97, weights: [0.0010099108330905437, 0.002730846870690584, 0.004451782442629337, 0.006172718480229378, 0.005150905344635248, 0.0034948070533573627, 0.0018387088784947991, 0.0001826105872169137,] },
|
|
1134
|
-
/* 63 */ { startIndex: 101, weights: [0.0012943691108375788, 0.002888072282075882, 0.004481775686144829, 0.006075479090213776, 0.0048866597935557365, 0.003353001084178686, 0.0018193417927250266, 0.00028568264679051936,] },
|
|
1135
|
-
/* 64 */ { startIndex: 105, weights: [0.0013131388695910573, 0.0027890161145478487, 0.004264893010258675, 0.0057407706044614315, 0.004859979264438152, 0.0034397069830447435, 0.0020194342359900475, 0.0005991620710119605,] },
|
|
1136
|
-
/* 65 */ { startIndex: 109, weights: [0.0011121684219688177, 0.002478930866345763, 0.0038456933107227087, 0.0052124555222690105, 0.005028639920055866, 0.0037133716978132725, 0.002398103242740035, 0.0010828346712514758,] },
|
|
1137
|
-
/* 66 */ { startIndex: 113, weights: [0.0007317548734135926, 0.0019974694587290287, 0.003263183869421482, 0.004528898745775223, 0.005355686880648136, 0.004137659445405006, 0.0029196315445005894, 0.0017016039928421378, 0.0004835762665607035,] },
|
|
1138
|
-
/* 67 */ { startIndex: 117, weights: [0.00020713974663522094, 0.0013792773243039846, 0.0025514145381748676, 0.003723552217707038, 0.004895689897239208, 0.004680895246565342, 0.0035529187880456448, 0.0024249425623565912, 0.0012969663366675377, 0.00016899015463422984,] },
|
|
1139
|
-
/* 68 */ { startIndex: 122, weights: [0.0006545265205204487, 0.0017400053329765797, 0.0028254841454327106, 0.003910962492227554, 0.004996441304683685, 0.0042709787376224995, 0.003226396394893527, 0.002181813819333911, 0.0011372314766049385, 9.264905384043232e-05,] },
|
|
1140
|
-
/* 69 */ { startIndex: 127, weights: [0.000854626705404371, 0.001859853626228869, 0.002865080488845706, 0.003870307235047221, 0.00487553421407938, 0.00408313749358058, 0.003115783678367734, 0.0021484296303242445, 0.001181075582280755, 0.0002137213887181133,] },
|
|
1141
|
-
/* 70 */ { startIndex: 132, weights: [0.0008483415003865957, 0.0017792496364563704, 0.0027101580053567886, 0.0036410661414265633, 0.004571974277496338, 0.004079728852957487, 0.003183893393725157, 0.002288057701662183, 0.0013922222424298525, 0.0004963868414051831,] },
|
|
1142
|
-
/* 71 */ { startIndex: 137, weights: [0.0006716204807162285, 0.0015337044605985284, 0.002395788673311472, 0.0032578727696090937, 0.004119956865906715, 0.004227725323289633, 0.0033981208689510822, 0.0025685166474431753, 0.0017389123095199466, 0.0009093079133890569, 7.970355363795534e-05,] },
|
|
1143
|
-
/* 72 */ { startIndex: 142, weights: [0.0003559796023182571, 0.0011543278815224767, 0.0019526762189343572, 0.002751024439930916, 0.0035493727773427963, 0.004347721114754677, 0.0037299629766494036, 0.002961693098768592, 0.00219342322088778, 0.0014251532265916467, 0.0006568834069184959,] },
|
|
1144
|
-
/* 73 */ { startIndex: 148, weights: [0.0006682946113869548, 0.0014076193328946829, 0.0021469437051564455, 0.002886268775910139, 0.0036255933810025454, 0.004154576454311609, 0.0034431067761033773, 0.0027316368650645018, 0.0020201667211949825, 0.0013086966937407851, 0.0005972267827019095,] },
|
|
1145
|
-
/* 74 */ { startIndex: 153, weights: [9.926508937496692e-05, 0.0007839298341423273, 0.001468594535253942, 0.0021532592363655567, 0.0028379240538924932, 0.0035225888714194298, 0.0039915177039802074, 0.0033326479606330395, 0.002673778682947159, 0.002014909405261278, 0.0013560398947447538, 0.0006971705006435513, 3.8301113818306476e-05,] },
|
|
1146
|
-
/* 75 */ { startIndex: 159, weights: [0.00010181095422012731, 0.0007358568836934865, 0.0013699028640985489, 0.0020039486698806286, 0.002637994708493352, 0.0032720407471060753, 0.003906086552888155, 0.0033682563807815313, 0.0027580985333770514, 0.002147940918803215, 0.0015377833042293787, 0.0009276255150325596, 0.000317467754939571,] },
|
|
1147
|
-
/* 76 */ { startIndex: 166, weights: [0.0005530364578589797, 0.0011402058880776167, 0.0017273754347115755, 0.0023145449813455343, 0.002901714527979493, 0.003488884074613452, 0.003523340215906501, 0.002958292607218027, 0.002393245231360197, 0.0018281979719176888, 0.001263150479644537, 0.0006981031037867069, 0.0001330557424807921,] },
|
|
1148
|
-
/* 77 */ { startIndex: 172, weights: [0.0002608386566862464, 0.0008045974536798894, 0.0013483562506735325, 0.0018921148730441928, 0.0024358737282454967, 0.002979632467031479, 0.003523391205817461, 0.003251380519941449, 0.0027281083166599274, 0.002204835880547762, 0.001681563793681562, 0.001158291706815362, 0.0006350195035338402, 0.00011174729297636077,] },
|
|
1149
|
-
/* 78 */ { startIndex: 179, weights: [0.0003849811910185963, 0.0008885387214832008, 0.001392096164636314, 0.0018956535495817661, 0.00239921105094254, 0.002902768552303314, 0.0034063260536640882, 0.003132763085886836, 0.0026481777895241976, 0.0021635922603309155, 0.0016790067311376333, 0.0011944210855290294, 0.0007098356145434082, 0.00022525011445395648,] },
|
|
1150
|
-
/* 79 */ { startIndex: 186, weights: [0.000366741674952209, 0.0008330700220540166, 0.0012993983691558242, 0.0017657268326729536, 0.0022320549469441175, 0.002698383294045925, 0.0031647118739783764, 0.003141313325613737, 0.002692554146051407, 0.0022437951993197203, 0.00179503601975739, 0.0013462770730257034, 0.000897518009878695, 0.0004487590049393475,] },
|
|
1151
|
-
];
|
|
1152
1197
|
export async function loadPackagesAndGetPaths(modelName, languageCode) {
|
|
1153
1198
|
if (modelName) {
|
|
1154
1199
|
modelName = normalizeWhisperModelName(modelName, languageCode);
|
|
@@ -1314,6 +1359,88 @@ const alignmentHeadsIndexes = {
|
|
|
1314
1359
|
'large-v3': [212, 277, 331, 332, 333, 355, 356, 364, 371, 379, 391, 422, 423, 443, 449, 452, 465, 467, 473, 505, 521, 532, 555], // Temporary (may not be correct)
|
|
1315
1360
|
'large': [212, 277, 331, 332, 333, 355, 356, 364, 371, 379, 391, 422, 423, 443, 449, 452, 465, 467, 473, 505, 521, 532, 555],
|
|
1316
1361
|
};
|
|
1362
|
+
const filterbanks = [
|
|
1363
|
+
/* 0 */ { startIndex: 1, weights: [0.02486259490251541,] },
|
|
1364
|
+
/* 1 */ { startIndex: 1, weights: [0.001990821911022067, 0.022871771827340126,] },
|
|
1365
|
+
/* 2 */ { startIndex: 2, weights: [0.003981643822044134, 0.02088095061480999,] },
|
|
1366
|
+
/* 3 */ { startIndex: 3, weights: [0.0059724655002355576, 0.018890129402279854,] },
|
|
1367
|
+
/* 4 */ { startIndex: 4, weights: [0.007963287644088268, 0.01689930632710457,] },
|
|
1368
|
+
/* 5 */ { startIndex: 5, weights: [0.009954108856618404, 0.014908484183251858,] },
|
|
1369
|
+
/* 6 */ { startIndex: 6, weights: [0.011944931000471115, 0.012917662039399147,] },
|
|
1370
|
+
/* 7 */ { startIndex: 7, weights: [0.013935752213001251, 0.010926840826869011,] },
|
|
1371
|
+
/* 8 */ { startIndex: 8, weights: [0.015926575288176537, 0.0089360186830163,] },
|
|
1372
|
+
/* 9 */ { startIndex: 9, weights: [0.017917396500706673, 0.006945197004824877,] },
|
|
1373
|
+
/* 10 */ { startIndex: 10, weights: [0.01990821771323681, 0.004954374860972166,] },
|
|
1374
|
+
/* 11 */ { startIndex: 11, weights: [0.021899040788412094, 0.0029635531827807426,] },
|
|
1375
|
+
/* 12 */ { startIndex: 12, weights: [0.02388986200094223, 0.0009727313299663365,] },
|
|
1376
|
+
/* 13 */ { startIndex: 13, weights: [0.025880683213472366,] },
|
|
1377
|
+
/* 14 */ { startIndex: 14, weights: [0.025835324078798294,] },
|
|
1378
|
+
/* 15 */ { startIndex: 14, weights: [0.0010180906392633915, 0.023844502866268158,] },
|
|
1379
|
+
/* 16 */ { startIndex: 15, weights: [0.003008912317454815, 0.021853681653738022,] },
|
|
1380
|
+
/* 17 */ { startIndex: 16, weights: [0.004999734461307526, 0.019862858578562737,] },
|
|
1381
|
+
/* 18 */ { startIndex: 17, weights: [0.006990555673837662, 0.0178720373660326,] },
|
|
1382
|
+
/* 19 */ { startIndex: 18, weights: [0.008981377817690372, 0.015881216153502464,] },
|
|
1383
|
+
/* 20 */ { startIndex: 19, weights: [0.010972199961543083, 0.013890394009649754,] },
|
|
1384
|
+
/* 21 */ { startIndex: 20, weights: [0.01296302117407322, 0.011899571865797043,] },
|
|
1385
|
+
/* 22 */ { startIndex: 21, weights: [0.01495384331792593, 0.009908749721944332,] },
|
|
1386
|
+
/* 23 */ { startIndex: 22, weights: [0.01694466546177864, 0.007917927578091621,] },
|
|
1387
|
+
/* 24 */ { startIndex: 23, weights: [0.018935488536953926, 0.005927106365561485,] },
|
|
1388
|
+
/* 25 */ { startIndex: 24, weights: [0.020874010398983955, 0.004040425643324852,] },
|
|
1389
|
+
/* 26 */ { startIndex: 25, weights: [0.022114217281341553, 0.0033186059445142746,] },
|
|
1390
|
+
/* 27 */ { startIndex: 26, weights: [0.02173672430217266, 0.0036109676584601402,] },
|
|
1391
|
+
/* 28 */ { startIndex: 27, weights: [0.020497702062129974, 0.004762193653732538,] },
|
|
1392
|
+
/* 29 */ { startIndex: 28, weights: [0.018486659973859787, 0.006592618301510811,] },
|
|
1393
|
+
/* 30 */ { startIndex: 29, weights: [0.01585603691637516, 0.00896277092397213,] },
|
|
1394
|
+
/* 31 */ { startIndex: 30, weights: [0.012738768011331558, 0.011751330457627773,] },
|
|
1395
|
+
/* 32 */ { startIndex: 31, weights: [0.009250369854271412, 0.014853144995868206,] },
|
|
1396
|
+
/* 33 */ { startIndex: 32, weights: [0.005490840878337622, 0.018177473917603493, 0.0028155462350696325,] },
|
|
1397
|
+
/* 34 */ { startIndex: 33, weights: [0.0015463664894923568, 0.01632951945066452, 0.007420188747346401,] },
|
|
1398
|
+
/* 35 */ { startIndex: 35, weights: [0.011181050911545753, 0.012018864043056965,] },
|
|
1399
|
+
/* 36 */ { startIndex: 36, weights: [0.006065350491553545, 0.016561277210712433, 0.004360878840088844,] },
|
|
1400
|
+
/* 37 */ { startIndex: 37, weights: [0.0010297985281795263, 0.012770536355674267, 0.009707189165055752,] },
|
|
1401
|
+
/* 38 */ { startIndex: 39, weights: [0.006986402906477451, 0.01485429983586073, 0.004391219466924667,] },
|
|
1402
|
+
/* 39 */ { startIndex: 40, weights: [0.001418047584593296, 0.011486922390758991, 0.010089744813740253, 0.00040022286702878773,] },
|
|
1403
|
+
/* 40 */ { startIndex: 42, weights: [0.005411104764789343, 0.014735566452145576, 0.006518189795315266,] },
|
|
1404
|
+
/* 41 */ { startIndex: 44, weights: [0.00827841367572546, 0.012277561239898205, 0.00396781275048852,] },
|
|
1405
|
+
/* 42 */ { startIndex: 45, weights: [0.002187808509916067, 0.010184479877352715, 0.00998187530785799, 0.0022864851634949446,] },
|
|
1406
|
+
/* 43 */ { startIndex: 47, weights: [0.00386943481862545, 0.011274894699454308, 0.008466221392154694, 0.0013397691072896123,] },
|
|
1407
|
+
/* 44 */ { startIndex: 49, weights: [0.004820294212549925, 0.011678251437842846, 0.007608682848513126, 0.0010091039584949613,] },
|
|
1408
|
+
/* 45 */ { startIndex: 51, weights: [0.005156961735337973, 0.011507894843816757, 0.007301822770386934, 0.0011901655234396458,] },
|
|
1409
|
+
/* 46 */ { startIndex: 53, weights: [0.004982104524970055, 0.010863498784601688, 0.007451189681887627, 0.001791381393559277,] },
|
|
1410
|
+
/* 47 */ { startIndex: 55, weights: [0.004385921638458967, 0.009832492098212242, 0.007973956875503063, 0.002732589840888977,] },
|
|
1411
|
+
/* 48 */ { startIndex: 57, weights: [0.0034474546555429697, 0.008491347543895245, 0.008797688409686089, 0.00394382793456316,] },
|
|
1412
|
+
/* 49 */ { startIndex: 59, weights: [0.0022357646375894547, 0.0069067515432834625, 0.009859241545200348, 0.005364237818866968, 0.0008692338014952838,] },
|
|
1413
|
+
/* 50 */ { startIndex: 61, weights: [0.0008110002381727099, 0.005136650986969471, 0.00946230161935091, 0.0069410777650773525, 0.0027783995028585196,] },
|
|
1414
|
+
/* 51 */ { startIndex: 64, weights: [0.003231203882023692, 0.007237049750983715, 0.00862883497029543, 0.004773912951350212, 0.0009189908159896731,] },
|
|
1415
|
+
/* 52 */ { startIndex: 66, weights: [0.001233637798577547, 0.0049433219246566296, 0.008653006516397, 0.006818502210080624, 0.003248583758249879,] },
|
|
1416
|
+
/* 53 */ { startIndex: 69, weights: [0.0026164355222135782, 0.006051854696124792, 0.008880467154085636, 0.005574479699134827, 0.002268492942675948,] },
|
|
1417
|
+
/* 54 */ { startIndex: 71, weights: [0.0002863667905330658, 0.003467798000201583, 0.0066492292098701, 0.00787146482616663, 0.004809896927326918, 0.0017483289120718837,] },
|
|
1418
|
+
/* 55 */ { startIndex: 74, weights: [0.0009245910914614797, 0.0038708120118826628, 0.00681703258305788, 0.007283343467861414, 0.004448124207556248, 0.0016129047144204378,] },
|
|
1419
|
+
/* 56 */ { startIndex: 77, weights: [0.0011703289346769452, 0.003898728871718049, 0.006627128925174475, 0.0070473202504217625, 0.004421714693307877, 0.0017961094854399562,] },
|
|
1420
|
+
/* 57 */ { startIndex: 80, weights: [0.0010892992140725255, 0.003615982597693801, 0.006142666097730398, 0.007102936040610075, 0.004671447444707155, 0.002239959081634879,] },
|
|
1421
|
+
/* 58 */ { startIndex: 83, weights: [0.0007392280967906117, 0.0030791081953793764, 0.005418988410383463, 0.007397185545414686, 0.005145462695509195, 0.002893739379942417, 0.0006420162972062826,] },
|
|
1422
|
+
/* 59 */ { startIndex: 86, weights: [0.00017068670422304422, 0.0023375742603093386, 0.004504461772739887, 0.0066713495180010796, 0.005798479542136192, 0.003713231300935149, 0.0016279831761494279,] },
|
|
1423
|
+
/* 60 */ { startIndex: 90, weights: [0.0014345343224704266, 0.0034412189852446318, 0.005447904113680124, 0.006591092795133591, 0.004660011734813452, 0.002728930441662669, 0.0007978491485118866,] },
|
|
1424
|
+
/* 61 */ { startIndex: 93, weights: [0.0004075043834745884, 0.002265830524265766, 0.004124156199395657, 0.005982482805848122, 0.005700822453945875, 0.003912510350346565, 0.0021241982467472553, 0.0003358862304594368,] },
|
|
1425
|
+
/* 62 */ { startIndex: 97, weights: [0.0010099108330905437, 0.002730846870690584, 0.004451782442629337, 0.006172718480229378, 0.005150905344635248, 0.0034948070533573627, 0.0018387088784947991, 0.0001826105872169137,] },
|
|
1426
|
+
/* 63 */ { startIndex: 101, weights: [0.0012943691108375788, 0.002888072282075882, 0.004481775686144829, 0.006075479090213776, 0.0048866597935557365, 0.003353001084178686, 0.0018193417927250266, 0.00028568264679051936,] },
|
|
1427
|
+
/* 64 */ { startIndex: 105, weights: [0.0013131388695910573, 0.0027890161145478487, 0.004264893010258675, 0.0057407706044614315, 0.004859979264438152, 0.0034397069830447435, 0.0020194342359900475, 0.0005991620710119605,] },
|
|
1428
|
+
/* 65 */ { startIndex: 109, weights: [0.0011121684219688177, 0.002478930866345763, 0.0038456933107227087, 0.0052124555222690105, 0.005028639920055866, 0.0037133716978132725, 0.002398103242740035, 0.0010828346712514758,] },
|
|
1429
|
+
/* 66 */ { startIndex: 113, weights: [0.0007317548734135926, 0.0019974694587290287, 0.003263183869421482, 0.004528898745775223, 0.005355686880648136, 0.004137659445405006, 0.0029196315445005894, 0.0017016039928421378, 0.0004835762665607035,] },
|
|
1430
|
+
/* 67 */ { startIndex: 117, weights: [0.00020713974663522094, 0.0013792773243039846, 0.0025514145381748676, 0.003723552217707038, 0.004895689897239208, 0.004680895246565342, 0.0035529187880456448, 0.0024249425623565912, 0.0012969663366675377, 0.00016899015463422984,] },
|
|
1431
|
+
/* 68 */ { startIndex: 122, weights: [0.0006545265205204487, 0.0017400053329765797, 0.0028254841454327106, 0.003910962492227554, 0.004996441304683685, 0.0042709787376224995, 0.003226396394893527, 0.002181813819333911, 0.0011372314766049385, 9.264905384043232e-05,] },
|
|
1432
|
+
/* 69 */ { startIndex: 127, weights: [0.000854626705404371, 0.001859853626228869, 0.002865080488845706, 0.003870307235047221, 0.00487553421407938, 0.00408313749358058, 0.003115783678367734, 0.0021484296303242445, 0.001181075582280755, 0.0002137213887181133,] },
|
|
1433
|
+
/* 70 */ { startIndex: 132, weights: [0.0008483415003865957, 0.0017792496364563704, 0.0027101580053567886, 0.0036410661414265633, 0.004571974277496338, 0.004079728852957487, 0.003183893393725157, 0.002288057701662183, 0.0013922222424298525, 0.0004963868414051831,] },
|
|
1434
|
+
/* 71 */ { startIndex: 137, weights: [0.0006716204807162285, 0.0015337044605985284, 0.002395788673311472, 0.0032578727696090937, 0.004119956865906715, 0.004227725323289633, 0.0033981208689510822, 0.0025685166474431753, 0.0017389123095199466, 0.0009093079133890569, 7.970355363795534e-05,] },
|
|
1435
|
+
/* 72 */ { startIndex: 142, weights: [0.0003559796023182571, 0.0011543278815224767, 0.0019526762189343572, 0.002751024439930916, 0.0035493727773427963, 0.004347721114754677, 0.0037299629766494036, 0.002961693098768592, 0.00219342322088778, 0.0014251532265916467, 0.0006568834069184959,] },
|
|
1436
|
+
/* 73 */ { startIndex: 148, weights: [0.0006682946113869548, 0.0014076193328946829, 0.0021469437051564455, 0.002886268775910139, 0.0036255933810025454, 0.004154576454311609, 0.0034431067761033773, 0.0027316368650645018, 0.0020201667211949825, 0.0013086966937407851, 0.0005972267827019095,] },
|
|
1437
|
+
/* 74 */ { startIndex: 153, weights: [9.926508937496692e-05, 0.0007839298341423273, 0.001468594535253942, 0.0021532592363655567, 0.0028379240538924932, 0.0035225888714194298, 0.0039915177039802074, 0.0033326479606330395, 0.002673778682947159, 0.002014909405261278, 0.0013560398947447538, 0.0006971705006435513, 3.8301113818306476e-05,] },
|
|
1438
|
+
/* 75 */ { startIndex: 159, weights: [0.00010181095422012731, 0.0007358568836934865, 0.0013699028640985489, 0.0020039486698806286, 0.002637994708493352, 0.0032720407471060753, 0.003906086552888155, 0.0033682563807815313, 0.0027580985333770514, 0.002147940918803215, 0.0015377833042293787, 0.0009276255150325596, 0.000317467754939571,] },
|
|
1439
|
+
/* 76 */ { startIndex: 166, weights: [0.0005530364578589797, 0.0011402058880776167, 0.0017273754347115755, 0.0023145449813455343, 0.002901714527979493, 0.003488884074613452, 0.003523340215906501, 0.002958292607218027, 0.002393245231360197, 0.0018281979719176888, 0.001263150479644537, 0.0006981031037867069, 0.0001330557424807921,] },
|
|
1440
|
+
/* 77 */ { startIndex: 172, weights: [0.0002608386566862464, 0.0008045974536798894, 0.0013483562506735325, 0.0018921148730441928, 0.0024358737282454967, 0.002979632467031479, 0.003523391205817461, 0.003251380519941449, 0.0027281083166599274, 0.002204835880547762, 0.001681563793681562, 0.001158291706815362, 0.0006350195035338402, 0.00011174729297636077,] },
|
|
1441
|
+
/* 78 */ { startIndex: 179, weights: [0.0003849811910185963, 0.0008885387214832008, 0.001392096164636314, 0.0018956535495817661, 0.00239921105094254, 0.002902768552303314, 0.0034063260536640882, 0.003132763085886836, 0.0026481777895241976, 0.0021635922603309155, 0.0016790067311376333, 0.0011944210855290294, 0.0007098356145434082, 0.00022525011445395648,] },
|
|
1442
|
+
/* 79 */ { startIndex: 186, weights: [0.000366741674952209, 0.0008330700220540166, 0.0012993983691558242, 0.0017657268326729536, 0.0022320549469441175, 0.002698383294045925, 0.0031647118739783764, 0.003141313325613737, 0.002692554146051407, 0.0022437951993197203, 0.00179503601975739, 0.0013462770730257034, 0.000897518009878695, 0.0004487590049393475,] },
|
|
1443
|
+
];
|
|
1317
1444
|
export const defaultWhisperOptions = {
|
|
1318
1445
|
model: undefined,
|
|
1319
1446
|
temperature: 0.1,
|
|
@@ -1324,6 +1451,28 @@ export const defaultWhisperOptions = {
|
|
|
1324
1451
|
maxTokensPerPart: 250,
|
|
1325
1452
|
suppressRepetition: true,
|
|
1326
1453
|
decodeTimestampTokens: true,
|
|
1454
|
+
endTokenThreshold: 0.9,
|
|
1455
|
+
includeEndTokenInCandidates: true,
|
|
1456
|
+
encoderProvider: undefined,
|
|
1457
|
+
decoderProvider: undefined,
|
|
1327
1458
|
seed: undefined,
|
|
1328
1459
|
};
|
|
1460
|
+
export const defaultWhisperAlignmentOptions = {
|
|
1461
|
+
model: undefined,
|
|
1462
|
+
endTokenThreshold: 0.9,
|
|
1463
|
+
encoderProvider: undefined,
|
|
1464
|
+
decoderProvider: undefined
|
|
1465
|
+
};
|
|
1466
|
+
export const defaultWhisperLanguageDetectionOptions = {
|
|
1467
|
+
model: undefined,
|
|
1468
|
+
temperature: 1.0,
|
|
1469
|
+
encoderProvider: undefined,
|
|
1470
|
+
decoderProvider: undefined,
|
|
1471
|
+
};
|
|
1472
|
+
export const defaultWhisperVADOptions = {
|
|
1473
|
+
model: undefined,
|
|
1474
|
+
temperature: 1.0,
|
|
1475
|
+
encoderProvider: undefined,
|
|
1476
|
+
decoderProvider: undefined,
|
|
1477
|
+
};
|
|
1329
1478
|
//# sourceMappingURL=WhisperSTT.js.map
|