echogarden 2.8.6 → 2.9.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/dist/api/Recognition.d.ts +3 -3
- package/dist/api/Recognition.js +5 -5
- package/dist/api/Recognition.js.map +1 -1
- package/dist/api/SpeechTranslation.d.ts +4 -4
- package/dist/api/SpeechTranslation.js +4 -4
- package/dist/api/SpeechTranslation.js.map +1 -1
- package/dist/denoising/NSNet2.js +8 -8
- package/dist/denoising/NSNet2.js.map +1 -1
- package/dist/dsp/FFT.d.ts +2 -2
- package/dist/dsp/FFT.js +19 -15
- package/dist/dsp/FFT.js.map +1 -1
- package/dist/dsp/MFCC.d.ts +1 -1
- package/dist/dsp/MFCC.js +8 -8
- package/dist/dsp/MFCC.js.map +1 -1
- package/dist/dsp/{MelSpectogram.d.ts → MelSpectrogram.d.ts} +5 -5
- package/dist/dsp/{MelSpectogram.js → MelSpectrogram.js} +9 -9
- package/dist/dsp/MelSpectrogram.js.map +1 -0
- package/dist/math/VectorMath.d.ts +5 -1
- package/dist/math/VectorMath.js +15 -2
- package/dist/math/VectorMath.js.map +1 -1
- package/dist/nlp/IPA.d.ts +3 -3
- package/dist/recognition/WhisperSTT.d.ts +9 -6
- package/dist/recognition/WhisperSTT.js +92 -60
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/source-separation/MDXNetSourceSeparation.js +5 -5
- package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
- package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js +14 -14
- package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js.map +1 -1
- package/dist/utilities/Utilities.d.ts +1 -1
- package/dist/utilities/Utilities.js +4 -2
- package/dist/utilities/Utilities.js.map +1 -1
- package/docs/Licenses.md +0 -2
- package/docs/Tasklist.md +18 -5
- package/package.json +14 -14
- package/src/api/Recognition.ts +10 -7
- package/src/api/SpeechTranslation.ts +16 -6
- package/src/denoising/NSNet2.ts +8 -8
- package/src/dsp/FFT.ts +23 -17
- package/src/dsp/MFCC.ts +8 -8
- package/src/dsp/{MelSpectogram.ts → MelSpectrogram.ts} +9 -8
- package/src/math/VectorMath.ts +22 -2
- package/src/recognition/WhisperSTT.ts +131 -65
- package/src/source-separation/MDXNetSourceSeparation.ts +5 -5
- package/src/speech-embeddings/WavToVec2BertFeatureEmbeddings.ts +14 -14
- package/src/utilities/Utilities.ts +4 -2
- package/dist/dsp/MelSpectogram.js.map +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
1
2
|
import type * as Onnx from 'onnxruntime-node'
|
|
2
3
|
|
|
3
4
|
import { Logger } from '../utilities/Logger.js'
|
|
4
|
-
import {
|
|
5
|
+
import { computeMelSpectrogramUsingFilterbanks, Filterbank } from '../dsp/MelSpectrogram.js'
|
|
5
6
|
import { clip, getIntegerRange, getTopKIndexes, splitFloat32Array, yieldToEventLoop } from '../utilities/Utilities.js'
|
|
6
|
-
import { indexOfMax, logOfVector, logSumExp, meanOfVector, softmax,
|
|
7
|
+
import { indexOfMax, logOfVector, logSumExp, meanOfVector, medianOfVector, softmax, sumAndSumOfSquaresOfVector, sumOfSquaresOfVector, sumVector } from '../math/VectorMath.js'
|
|
7
8
|
|
|
8
9
|
import { alignDTWWindowed } from '../alignment/DTWSequenceAlignmentWindowed.js'
|
|
9
10
|
import { extendDeep } from '../utilities/ObjectUtilities.js'
|
|
@@ -11,10 +12,9 @@ import { Timeline, TimelineEntry } from '../utilities/Timeline.js'
|
|
|
11
12
|
import { AlignmentPath } from '../alignment/SpeechAlignment.js'
|
|
12
13
|
import { getRawAudioDuration, RawAudio, sliceRawAudio } from '../audio/AudioUtilities.js'
|
|
13
14
|
import { readFileAsUtf8 } from '../utilities/FileSystem.js'
|
|
14
|
-
import type
|
|
15
|
+
import { logLevelGreaterOrEqualTo, type LanguageDetectionResults } from '../api/API.js'
|
|
15
16
|
import { formatLanguageCodeWithName, getShortLanguageCode, languageCodeToName } from '../utilities/Locale.js'
|
|
16
17
|
import { loadPackage } from '../utilities/PackageManager.js'
|
|
17
|
-
import chalk from 'chalk'
|
|
18
18
|
import { XorShift32PRNG } from '../utilities/RandomGenerator.js'
|
|
19
19
|
import { detectSpeechLanguageByParts } from '../api/SpeechLanguageDetection.js'
|
|
20
20
|
import { type Tiktoken } from 'tiktoken/lite'
|
|
@@ -25,6 +25,7 @@ import { dmlProviderAvailable, getOnnxSessionOptions, makeOnnxLikeFloat32Tensor,
|
|
|
25
25
|
import { murmurHash3_int32Input } from '../utilities/Hashing.js'
|
|
26
26
|
import { containsInvalidCodepoint, getTokenRepetitionScore } from '../utilities/StringUtilities.js'
|
|
27
27
|
import { joinPath } from '../utilities/PathUtilities.js'
|
|
28
|
+
import { Timer } from '../utilities/Timer.js'
|
|
28
29
|
|
|
29
30
|
export async function recognize(
|
|
30
31
|
sourceRawAudio: RawAudio,
|
|
@@ -32,11 +33,12 @@ export async function recognize(
|
|
|
32
33
|
modelDir: string,
|
|
33
34
|
task: WhisperTask,
|
|
34
35
|
sourceLanguage: string,
|
|
35
|
-
options: WhisperOptions
|
|
36
|
+
options: WhisperOptions,
|
|
37
|
+
onPart?: WhisperPartCallback) {
|
|
36
38
|
|
|
37
39
|
options = extendDeep(defaultWhisperOptions, options)
|
|
38
40
|
|
|
39
|
-
if (sourceRawAudio.sampleRate
|
|
41
|
+
if (sourceRawAudio.sampleRate !== 16000) {
|
|
40
42
|
throw new Error('Source audio must have a sample rate of 16000 Hz')
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -46,7 +48,7 @@ export async function recognize(
|
|
|
46
48
|
throw new Error(`The language ${formatLanguageCodeWithName(sourceLanguage)} is not supported by the Whisper engine.`)
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
if (isEnglishOnlyModel(modelName) && sourceLanguage
|
|
51
|
+
if (isEnglishOnlyModel(modelName) && sourceLanguage !== 'en') {
|
|
50
52
|
throw new Error(`The model '${modelName}' can only be used with English inputs. However, the given source language was ${languageCodeToName(sourceLanguage)}.`)
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -60,7 +62,7 @@ export async function recognize(
|
|
|
60
62
|
|
|
61
63
|
// Workaround issue with large-v3-turbo that produces invalid results when a prompt is passed to it.
|
|
62
64
|
// Always disable autoprompting for that model.
|
|
63
|
-
if (options.autoPromptParts && modelName
|
|
65
|
+
if (options.autoPromptParts && modelName === 'large-v3-turbo') {
|
|
64
66
|
options.autoPromptParts = false
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -81,7 +83,7 @@ export async function recognize(
|
|
|
81
83
|
decoderProviders,
|
|
82
84
|
seed)
|
|
83
85
|
|
|
84
|
-
const result = await whisper.recognize(sourceRawAudio, task, sourceLanguage, options)
|
|
86
|
+
const result = await whisper.recognize(sourceRawAudio, task, sourceLanguage, options, undefined, onPart)
|
|
85
87
|
|
|
86
88
|
return result
|
|
87
89
|
}
|
|
@@ -96,7 +98,7 @@ export async function align(
|
|
|
96
98
|
|
|
97
99
|
options = extendDeep(defaultWhisperAlignmentOptions, options)
|
|
98
100
|
|
|
99
|
-
if (sourceRawAudio.sampleRate
|
|
101
|
+
if (sourceRawAudio.sampleRate !== 16000) {
|
|
100
102
|
throw new Error('Source audio must have a sample rate of 16000 Hz')
|
|
101
103
|
}
|
|
102
104
|
|
|
@@ -106,7 +108,7 @@ export async function align(
|
|
|
106
108
|
throw new Error(`The language ${formatLanguageCodeWithName(sourceLanguage)} is not supported by the Whisper engine.`)
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
if (isEnglishOnlyModel(modelName) && sourceLanguage
|
|
111
|
+
if (isEnglishOnlyModel(modelName) && sourceLanguage !== 'en') {
|
|
110
112
|
throw new Error(`The model '${modelName}' can only be used with English inputs. However, the given source language was ${languageCodeToName(sourceLanguage)}.`)
|
|
111
113
|
}
|
|
112
114
|
|
|
@@ -139,7 +141,7 @@ export async function alignEnglishTranslation(
|
|
|
139
141
|
|
|
140
142
|
options = extendDeep(defaultWhisperAlignmentOptions, options)
|
|
141
143
|
|
|
142
|
-
if (sourceRawAudio.sampleRate
|
|
144
|
+
if (sourceRawAudio.sampleRate !== 16000) {
|
|
143
145
|
throw new Error('Source audio must have a sample rate of 16000 Hz')
|
|
144
146
|
}
|
|
145
147
|
|
|
@@ -184,7 +186,7 @@ export async function detectLanguage(
|
|
|
184
186
|
|
|
185
187
|
options = extendDeep(defaultWhisperLanguageDetectionOptions, options)
|
|
186
188
|
|
|
187
|
-
if (sourceRawAudio.sampleRate
|
|
189
|
+
if (sourceRawAudio.sampleRate !== 16000) {
|
|
188
190
|
throw new Error('Source audio must have a sample rate of 16000 Hz')
|
|
189
191
|
}
|
|
190
192
|
|
|
@@ -232,7 +234,7 @@ export async function detectVoiceActivity(
|
|
|
232
234
|
|
|
233
235
|
options = extendDeep(defaultWhisperVADOptions, options)
|
|
234
236
|
|
|
235
|
-
if (sourceRawAudio.sampleRate
|
|
237
|
+
if (sourceRawAudio.sampleRate !== 16000) {
|
|
236
238
|
throw new Error('Source audio must have a sample rate of 16000 Hz')
|
|
237
239
|
}
|
|
238
240
|
|
|
@@ -365,6 +367,7 @@ export class Whisper {
|
|
|
365
367
|
language: string,
|
|
366
368
|
options: WhisperOptions,
|
|
367
369
|
logitFilter?: WhisperLogitFilter,
|
|
370
|
+
onPart?: WhisperPartCallback,
|
|
368
371
|
) {
|
|
369
372
|
await this.initializeIfNeeded()
|
|
370
373
|
|
|
@@ -441,7 +444,11 @@ export class Whisper {
|
|
|
441
444
|
let {
|
|
442
445
|
decodedTokens: partTokens,
|
|
443
446
|
decodedTokensConfidence: partTokensConfidence,
|
|
444
|
-
decodedTokensCrossAttentionQKs:
|
|
447
|
+
decodedTokensCrossAttentionQKs: partTokensCrossAttentionQKs,
|
|
448
|
+
|
|
449
|
+
decodedTokensDecodingTime: partTokensDecodingTime,
|
|
450
|
+
decodedTokensInferenceTime: partTokensInferenceTime,
|
|
451
|
+
decodedTokensOverheadTime: partTokensOverheadTime,
|
|
445
452
|
} = await this.decodeTokens(
|
|
446
453
|
audioPartFeatures,
|
|
447
454
|
initialTokens,
|
|
@@ -470,19 +477,19 @@ export class Whisper {
|
|
|
470
477
|
|
|
471
478
|
await logger.startAsync(`Extract timeline for part (timestamp accuracy: ${options.timestampAccuracy!})`)
|
|
472
479
|
|
|
473
|
-
if (partTokens.length
|
|
474
|
-
throw new Error('Unexpected: partTokens.length
|
|
480
|
+
if (partTokens.length !== partTokensCrossAttentionQKs.length) {
|
|
481
|
+
throw new Error('Unexpected: partTokens.length !== partCrossAttentionQKs.length')
|
|
475
482
|
}
|
|
476
483
|
|
|
477
484
|
// Prepare tokens
|
|
478
485
|
partTokens = partTokens.slice(initialTokens.length)
|
|
479
486
|
partTokensConfidence = partTokensConfidence.slice(initialTokens.length)
|
|
480
|
-
|
|
487
|
+
partTokensCrossAttentionQKs = partTokensCrossAttentionQKs.slice(initialTokens.length)
|
|
481
488
|
|
|
482
489
|
// Find alignment path
|
|
483
490
|
let alignmentHeads: number[] | undefined
|
|
484
491
|
|
|
485
|
-
if (options.timestampAccuracy === 'medium' || options.model
|
|
492
|
+
if (options.timestampAccuracy === 'medium' || options.model === 'large-v3-turbo') {
|
|
486
493
|
alignmentHeads = this.alignmentHeadIndexes
|
|
487
494
|
} else if (options.timestampAccuracy === 'high') {
|
|
488
495
|
alignmentHeads = undefined
|
|
@@ -490,11 +497,18 @@ export class Whisper {
|
|
|
490
497
|
throw new Error(`Unsupported timestamp accuracy '${options.timestampAccuracy}', can only be 'medium' or 'high'.`)
|
|
491
498
|
}
|
|
492
499
|
|
|
493
|
-
const alignmentPath = await this.findAlignmentPathFromQKs(
|
|
500
|
+
const alignmentPath = await this.findAlignmentPathFromQKs(partTokensCrossAttentionQKs, partTokens, 0, segmentFrameCount, alignmentHeads)
|
|
494
501
|
|
|
495
502
|
// Generate timeline from alignment path
|
|
496
503
|
const partTimeline = await this.getTokenTimelineFromAlignmentPath(alignmentPath, partTokens, segmentStartTime, segmentEndTime, partTokensConfidence)
|
|
497
504
|
|
|
505
|
+
if (onPart) {
|
|
506
|
+
const partWordTimeline = this.tokenTimelineToWordTimeline(partTimeline, language)
|
|
507
|
+
const partTranscript = this.tokensToText(partTokens)
|
|
508
|
+
|
|
509
|
+
onPart(partTranscript, partTimeline, partWordTimeline)
|
|
510
|
+
}
|
|
511
|
+
|
|
498
512
|
// Add tokens to output
|
|
499
513
|
allDecodedTokens.push(...partTokens)
|
|
500
514
|
timeline.push(...partTimeline)
|
|
@@ -514,6 +528,15 @@ export class Whisper {
|
|
|
514
528
|
audioOffset = audioEndOffset
|
|
515
529
|
|
|
516
530
|
logger.end()
|
|
531
|
+
|
|
532
|
+
if (logLevelGreaterOrEqualTo('trace')) {
|
|
533
|
+
const promptDecodingTime = partTokensDecodingTime[0]
|
|
534
|
+
const medianTokenDecodingTime = medianOfVector(partTokensDecodingTime.slice(1))
|
|
535
|
+
const medianTokenInferenceTime = medianOfVector(partTokensInferenceTime.slice(1))
|
|
536
|
+
const medianOverheadTime = medianOfVector(partTokensOverheadTime.slice(1))
|
|
537
|
+
|
|
538
|
+
logger.log(`${chalk.blueBright('Context')}: ${initialTokens.length + partTokens.length} tokens (${initialTokens.length} prompt, ${partTokens.length} decoded)\n${chalk.blueBright('Prompt decode time')}: ${promptDecodingTime.toFixed(1)}ms\n${chalk.blueBright('Median token decode time')}: ${medianTokenDecodingTime.toFixed(1)}ms (${medianTokenInferenceTime.toFixed(1)}ms inference, ${medianOverheadTime.toFixed(2)}ms overhead)`, 'trace')
|
|
539
|
+
}
|
|
517
540
|
}
|
|
518
541
|
|
|
519
542
|
// Convert token timeline to word timeline
|
|
@@ -812,8 +835,17 @@ export class Whisper {
|
|
|
812
835
|
|
|
813
836
|
const maxTokensPerPart = Math.min(options.maxTokensPerPart!, largestMaximumTokensPerPart)
|
|
814
837
|
|
|
838
|
+
let decodedTokensInferenceTime: number[] = []
|
|
839
|
+
let decodedTokensDecodingTime: number[] = []
|
|
840
|
+
|
|
841
|
+
const tokenDecodingTimeTimer = new Timer()
|
|
842
|
+
|
|
815
843
|
// Start decoding loop
|
|
816
844
|
for (let decodedTokenCount = 0; decodedTokenCount < maxTokensPerPart; decodedTokenCount++) {
|
|
845
|
+
if (decodedTokenCount > 0) {
|
|
846
|
+
decodedTokensDecodingTime.push(tokenDecodingTimeTimer.getElapsedTimeAndRestart())
|
|
847
|
+
}
|
|
848
|
+
|
|
817
849
|
const isInitialState = decodedTokens.length === initialTokens.length
|
|
818
850
|
const atLeastOneTextTokenDecoded = decodedTokens.slice(initialTokens.length).some(token => this.isTextToken(token))
|
|
819
851
|
|
|
@@ -847,9 +879,13 @@ export class Whisper {
|
|
|
847
879
|
offset: offsetTensor
|
|
848
880
|
}
|
|
849
881
|
|
|
850
|
-
|
|
882
|
+
//// Infer with ONNX decoder model
|
|
883
|
+
const tokenInferenceTimeTimer = new Timer()
|
|
884
|
+
|
|
851
885
|
const decoderOutputs = await this.textDecoder!.run(decoderInputs)
|
|
852
886
|
|
|
887
|
+
decodedTokensInferenceTime.push(tokenInferenceTimeTimer.elapsedTime)
|
|
888
|
+
|
|
853
889
|
// Extract decoder model results
|
|
854
890
|
const logitsBuffer = decoderOutputs['logits'].data as Float32Array
|
|
855
891
|
kvCacheTensor = decoderOutputs['output_kv_cache'] as any
|
|
@@ -1142,6 +1178,10 @@ export class Whisper {
|
|
|
1142
1178
|
await yieldToEventLoop()
|
|
1143
1179
|
}
|
|
1144
1180
|
|
|
1181
|
+
if (decodedTokensDecodingTime.length === decodedTokensInferenceTime.length - 1) {
|
|
1182
|
+
decodedTokensDecodingTime.push(tokenDecodingTimeTimer.getElapsedTimeAndRestart())
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1145
1185
|
// If at least two timestamp tokens were decoded and it's not the final part,
|
|
1146
1186
|
// truncate up to the last timestamp token
|
|
1147
1187
|
if (timestampTokenSeenCount >= 2 && !isFinalPart) {
|
|
@@ -1151,8 +1191,13 @@ export class Whisper {
|
|
|
1151
1191
|
decodedTokensTimestampLogits = decodedTokensTimestampLogits.slice(0, sliceEndTokenIndex)
|
|
1152
1192
|
decodedTokensCrossAttentionQKs = decodedTokensCrossAttentionQKs.slice(0, sliceEndTokenIndex)
|
|
1153
1193
|
decodedTokensConfidence = decodedTokensConfidence.slice(0, sliceEndTokenIndex)
|
|
1194
|
+
|
|
1195
|
+
decodedTokensDecodingTime = decodedTokensDecodingTime.slice(0, sliceEndTokenIndex)
|
|
1196
|
+
decodedTokensInferenceTime = decodedTokensInferenceTime.slice(0, sliceEndTokenIndex)
|
|
1154
1197
|
}
|
|
1155
1198
|
|
|
1199
|
+
const decodedTokensOverheadTime = decodedTokensDecodingTime.map((time, index) => time - decodedTokensInferenceTime[index])
|
|
1200
|
+
|
|
1156
1201
|
logger.write('\n')
|
|
1157
1202
|
logger.end()
|
|
1158
1203
|
|
|
@@ -1162,6 +1207,10 @@ export class Whisper {
|
|
|
1162
1207
|
decodedTokensTimestampLogits,
|
|
1163
1208
|
decodedTokensConfidence,
|
|
1164
1209
|
decodedTokensCrossAttentionQKs,
|
|
1210
|
+
|
|
1211
|
+
decodedTokensDecodingTime,
|
|
1212
|
+
decodedTokensInferenceTime,
|
|
1213
|
+
decodedTokensOverheadTime,
|
|
1165
1214
|
}
|
|
1166
1215
|
}
|
|
1167
1216
|
|
|
@@ -1186,7 +1235,7 @@ export class Whisper {
|
|
|
1186
1235
|
const maxAudioSamples = sampleRate * 30
|
|
1187
1236
|
const maxAudioFrames = 3000
|
|
1188
1237
|
|
|
1189
|
-
if (sampleRate
|
|
1238
|
+
if (sampleRate !== 16000) {
|
|
1190
1239
|
throw new Error('Audio must have a sample rate of 16000 Hz')
|
|
1191
1240
|
}
|
|
1192
1241
|
|
|
@@ -1194,8 +1243,7 @@ export class Whisper {
|
|
|
1194
1243
|
throw new Error(`Audio part is longer than 30 seconds`)
|
|
1195
1244
|
}
|
|
1196
1245
|
|
|
1197
|
-
|
|
1198
|
-
await logger.startAsync('Extract mel spectogram from audio part')
|
|
1246
|
+
await logger.startAsync('Extract Mel spectrogram from audio part')
|
|
1199
1247
|
|
|
1200
1248
|
// Pad audio samples to ensure that have a duration of 30 seconds
|
|
1201
1249
|
const paddedAudioSamples = new Float32Array(maxAudioSamples)
|
|
@@ -1203,40 +1251,40 @@ export class Whisper {
|
|
|
1203
1251
|
|
|
1204
1252
|
const rawAudioPart: RawAudio = { audioChannels: [paddedAudioSamples], sampleRate }
|
|
1205
1253
|
|
|
1206
|
-
|
|
1254
|
+
// Compute Mel spectrogram
|
|
1255
|
+
const { melSpectrogram } = await computeMelSpectrogramUsingFilterbanks(rawAudioPart, fftOrder, fftWindowSize, fftHopLength, filterbanks)
|
|
1207
1256
|
|
|
1208
|
-
|
|
1257
|
+
// Flatten, transpose, apply logarithm and normalize Mel spectrogram
|
|
1258
|
+
await logger.startAsync('Process Mel spectrogram')
|
|
1209
1259
|
|
|
1210
|
-
const
|
|
1260
|
+
const flattenedLogMelSpectrogram = new Float32Array(maxAudioFrames * filterbankCount)
|
|
1211
1261
|
|
|
1212
|
-
// Find maximum log mel value in the spectrum
|
|
1213
1262
|
let maxLogMel = -Infinity
|
|
1214
1263
|
|
|
1215
|
-
for (
|
|
1216
|
-
for (
|
|
1217
|
-
|
|
1218
|
-
|
|
1264
|
+
for (let i = 0; i < filterbankCount; i++) {
|
|
1265
|
+
for (let j = 0; j < maxAudioFrames; j++) {
|
|
1266
|
+
const mel = melSpectrogram[j][i]
|
|
1267
|
+
const logMel = Math.log10(Math.max(mel, 1e-10))
|
|
1268
|
+
|
|
1269
|
+
if (logMel > maxLogMel) {
|
|
1270
|
+
maxLogMel = logMel
|
|
1219
1271
|
}
|
|
1272
|
+
|
|
1273
|
+
flattenedLogMelSpectrogram[(i * maxAudioFrames) + j] = logMel
|
|
1220
1274
|
}
|
|
1221
1275
|
}
|
|
1222
1276
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
// Flatten the normalized log mel spectogram
|
|
1228
|
-
const flattenedNormalizedLogMelSpectogram = new Float32Array(maxAudioFrames * filterbankCount)
|
|
1277
|
+
for (let i = 0; i < flattenedLogMelSpectrogram.length; i++) {
|
|
1278
|
+
const logMel = flattenedLogMelSpectrogram[i]
|
|
1279
|
+
const normalizedLogMel = (Math.max(logMel, maxLogMel - 8) + 4) / 4
|
|
1229
1280
|
|
|
1230
|
-
|
|
1231
|
-
for (let j = 0; j < maxAudioFrames; j++) {
|
|
1232
|
-
flattenedNormalizedLogMelSpectogram[(i * maxAudioFrames) + j] = normalizedLogMelSpectogram[j][i]
|
|
1233
|
-
}
|
|
1281
|
+
flattenedLogMelSpectrogram[i] = normalizedLogMel
|
|
1234
1282
|
}
|
|
1235
1283
|
|
|
1236
1284
|
// Run the encoder model
|
|
1237
|
-
await logger.startAsync('Encode
|
|
1285
|
+
await logger.startAsync('Encode Mel spectrogram with Whisper encoder model')
|
|
1238
1286
|
|
|
1239
|
-
const inputTensor = new Onnx.Tensor('float32',
|
|
1287
|
+
const inputTensor = new Onnx.Tensor('float32', flattenedLogMelSpectrogram, [1, filterbankCount, maxAudioFrames])
|
|
1240
1288
|
|
|
1241
1289
|
const encoderInputs = { mel: inputTensor }
|
|
1242
1290
|
|
|
@@ -1267,7 +1315,7 @@ export class Whisper {
|
|
|
1267
1315
|
return isSeparatorCharacter(text[text.length - 1])
|
|
1268
1316
|
}
|
|
1269
1317
|
|
|
1270
|
-
if (language
|
|
1318
|
+
if (language !== 'zh' && language !== 'ja') {
|
|
1271
1319
|
tokenTimeline = tokenTimeline.filter(entry => this.isTextToken(entry.id!))
|
|
1272
1320
|
}
|
|
1273
1321
|
|
|
@@ -1282,7 +1330,7 @@ export class Whisper {
|
|
|
1282
1330
|
const text = entry.text
|
|
1283
1331
|
const previousEntryText = previousEntry?.text
|
|
1284
1332
|
|
|
1285
|
-
if (groups.length
|
|
1333
|
+
if (groups.length === 0 ||
|
|
1286
1334
|
text === '' ||
|
|
1287
1335
|
startsWithSeparatorCharacter(text) ||
|
|
1288
1336
|
(previousEntryText != null && endsWithSeparatorCharacter(previousEntryText))) {
|
|
@@ -1346,14 +1394,14 @@ export class Whisper {
|
|
|
1346
1394
|
}
|
|
1347
1395
|
|
|
1348
1396
|
async getTokenTimelineFromAlignmentPath(alignmentPath: AlignmentPath, tokens: number[], startTimeOffset: number, endTimeOffset: number, tokensConfidence?: number[], correctionAmount = 0.0) {
|
|
1349
|
-
if (alignmentPath.length
|
|
1397
|
+
if (alignmentPath.length === 0) {
|
|
1350
1398
|
return []
|
|
1351
1399
|
}
|
|
1352
1400
|
|
|
1353
1401
|
const tokenTimeline: Timeline = []
|
|
1354
1402
|
|
|
1355
1403
|
for (let pathIndex = 0; pathIndex < alignmentPath.length; pathIndex++) {
|
|
1356
|
-
if (pathIndex
|
|
1404
|
+
if (pathIndex !== 0 && alignmentPath[pathIndex].source === alignmentPath[pathIndex - 1].source) {
|
|
1357
1405
|
continue
|
|
1358
1406
|
}
|
|
1359
1407
|
|
|
@@ -1448,8 +1496,10 @@ export class Whisper {
|
|
|
1448
1496
|
let countOfAllWeightsForHead = 0
|
|
1449
1497
|
|
|
1450
1498
|
for (const tokenFrames of head) {
|
|
1451
|
-
|
|
1452
|
-
|
|
1499
|
+
const { sum, sumOfSquares } = sumAndSumOfSquaresOfVector(tokenFrames)
|
|
1500
|
+
|
|
1501
|
+
sumOfAllWeightsForHead += sum
|
|
1502
|
+
sumOfAllSquaredWeightsForHead += sumOfSquares
|
|
1453
1503
|
countOfAllWeightsForHead += tokenFrames.length
|
|
1454
1504
|
}
|
|
1455
1505
|
|
|
@@ -1604,7 +1654,7 @@ export class Whisper {
|
|
|
1604
1654
|
|
|
1605
1655
|
const onnxSessionOptions = getOnnxSessionOptions({ executionProviders: this.encoderExecutionProviders })
|
|
1606
1656
|
|
|
1607
|
-
const onnxProvidersString = onnxSessionOptions.executionProviders!.length > 0 ? `${
|
|
1657
|
+
const onnxProvidersString = onnxSessionOptions.executionProviders!.length > 0 ? `${onnxSessionOptions.executionProviders!.join(', ')}` : `default`
|
|
1608
1658
|
|
|
1609
1659
|
await logger.startAsync(`Create encoder inference session for model '${this.modelName}' (ONNX provider: ${onnxProvidersString})`)
|
|
1610
1660
|
|
|
@@ -1642,15 +1692,15 @@ export class Whisper {
|
|
|
1642
1692
|
getKvDimensions(groupCount: number, length: number) {
|
|
1643
1693
|
const modelName = this.modelName
|
|
1644
1694
|
|
|
1645
|
-
if (modelName
|
|
1695
|
+
if (modelName === 'tiny' || modelName === 'tiny.en') {
|
|
1646
1696
|
return [8, groupCount, length, 384]
|
|
1647
|
-
} else if (modelName
|
|
1697
|
+
} else if (modelName === 'base' || modelName === 'base.en') {
|
|
1648
1698
|
return [12, groupCount, length, 512]
|
|
1649
|
-
} else if (modelName
|
|
1699
|
+
} else if (modelName === 'small' || modelName === 'small.en') {
|
|
1650
1700
|
return [24, groupCount, length, 768]
|
|
1651
|
-
} else if (modelName
|
|
1701
|
+
} else if (modelName === 'medium' || modelName === 'medium.en') {
|
|
1652
1702
|
return [48, groupCount, length, 1024]
|
|
1653
|
-
} else if (modelName
|
|
1703
|
+
} else if (modelName === 'large-v1' || modelName === 'large-v2' || modelName === 'large-v3' || modelName === 'large-v3-turbo') {
|
|
1654
1704
|
return [64, groupCount, length, 1280]
|
|
1655
1705
|
} else {
|
|
1656
1706
|
throw new Error(`Unsupported model: ${modelName}`)
|
|
@@ -1664,7 +1714,7 @@ export class Whisper {
|
|
|
1664
1714
|
|
|
1665
1715
|
if (this.isMultiligualModel) {
|
|
1666
1716
|
const languageToken = this.tokenConfig.languageTokensStart + languageIdLookup[language]
|
|
1667
|
-
const taskToken = task
|
|
1717
|
+
const taskToken = task === 'translate' ? this.tokenConfig.translateTaskToken : this.tokenConfig.transcribeTaskToken
|
|
1668
1718
|
|
|
1669
1719
|
tokens = [startOfTextToken, languageToken, taskToken]
|
|
1670
1720
|
} else {
|
|
@@ -1900,7 +1950,7 @@ export async function loadPackagesAndGetPaths(modelName: WhisperModelName | unde
|
|
|
1900
1950
|
if (languageCode) {
|
|
1901
1951
|
const shortLanguageCode = getShortLanguageCode(languageCode)
|
|
1902
1952
|
|
|
1903
|
-
modelName = shortLanguageCode
|
|
1953
|
+
modelName = shortLanguageCode === 'en' ? 'tiny.en' : 'tiny'
|
|
1904
1954
|
} else {
|
|
1905
1955
|
modelName = 'tiny'
|
|
1906
1956
|
}
|
|
@@ -1918,7 +1968,7 @@ export async function loadPackagesAndGetPaths(modelName: WhisperModelName | unde
|
|
|
1918
1968
|
}
|
|
1919
1969
|
|
|
1920
1970
|
export function normalizeWhisperModelName(modelName: WhisperModelName, languageCode: string | undefined): WhisperModelName {
|
|
1921
|
-
if (languageCode
|
|
1971
|
+
if (languageCode !== 'en' && modelName.endsWith('.en')) {
|
|
1922
1972
|
const originalModelName = modelName
|
|
1923
1973
|
modelName = modelName.slice(0, modelName.length - 3) as WhisperModelName
|
|
1924
1974
|
|
|
@@ -1964,7 +2014,20 @@ export type WhisperTokenData = {
|
|
|
1964
2014
|
|
|
1965
2015
|
export type WhisperLogitFilter = (logits: number[], decodedTokens: number[], isFirstPart: boolean, isFinalPart: boolean) => number[]
|
|
1966
2016
|
|
|
1967
|
-
export type WhisperModelName =
|
|
2017
|
+
export type WhisperModelName =
|
|
2018
|
+
'tiny' |
|
|
2019
|
+
'tiny.en' |
|
|
2020
|
+
'base' |
|
|
2021
|
+
'base.en' |
|
|
2022
|
+
'small' |
|
|
2023
|
+
'small.en' |
|
|
2024
|
+
'medium' |
|
|
2025
|
+
'medium.en' |
|
|
2026
|
+
'large-v1' |
|
|
2027
|
+
'large-v2' |
|
|
2028
|
+
'large-v3' |
|
|
2029
|
+
'large-v3-turbo'
|
|
2030
|
+
|
|
1968
2031
|
export type WhisperTask = 'transcribe' | 'translate' | 'detect-language'
|
|
1969
2032
|
|
|
1970
2033
|
export const modelNameToPackageName: { [modelName in WhisperModelName]: string } = {
|
|
@@ -2084,17 +2147,18 @@ const languageIdLookup: { [s: string]: number } = {
|
|
|
2084
2147
|
'ba': 96,
|
|
2085
2148
|
'jw': 97,
|
|
2086
2149
|
'su': 98,
|
|
2150
|
+
//'yue': 99
|
|
2087
2151
|
}
|
|
2088
2152
|
|
|
2089
2153
|
const alignmentHeadsIndexes: { [name in WhisperModelName]: number[] } = {
|
|
2090
|
-
'tiny.en': [6, 12, 17, 18, 19, 20, 21, 22,],
|
|
2091
2154
|
'tiny': [14, 18, 20, 21, 22, 23,],
|
|
2092
|
-
'
|
|
2155
|
+
'tiny.en': [6, 12, 17, 18, 19, 20, 21, 22,],
|
|
2093
2156
|
'base': [25, 34, 35, 39, 41, 42, 44, 46,],
|
|
2094
|
-
'
|
|
2157
|
+
'base.en': [27, 39, 41, 45, 47,],
|
|
2095
2158
|
'small': [63, 69, 96, 100, 103, 104, 108, 115, 117, 125,],
|
|
2096
|
-
'
|
|
2159
|
+
'small.en': [78, 84, 87, 92, 98, 101, 103, 108, 112, 116, 118, 120, 121, 122, 123, 126, 131, 134, 136,],
|
|
2097
2160
|
'medium': [223, 244, 255, 257, 320, 372,],
|
|
2161
|
+
'medium.en': [180, 225, 236, 238, 244, 256, 260, 265, 284, 286, 295, 298, 303, 320, 323, 329, 334, 348,],
|
|
2098
2162
|
'large-v1': [199, 222, 224, 237, 447, 451, 457, 462, 475,],
|
|
2099
2163
|
'large-v2': [212, 277, 331, 332, 333, 355, 356, 364, 371, 379, 391, 422, 423, 443, 449, 452, 465, 467, 473, 505, 521, 532, 555,],
|
|
2100
2164
|
'large-v3': [140, 217, 258, 272, 321, 354, 391, 424, 481, 506,],
|
|
@@ -2613,4 +2677,6 @@ export const defaultWhisperVADOptions: WhisperVADOptions = {
|
|
|
2613
2677
|
decoderProvider: undefined,
|
|
2614
2678
|
}
|
|
2615
2679
|
|
|
2616
|
-
type WhisperTimestampAccuracy = 'medium' | 'high'
|
|
2680
|
+
export type WhisperTimestampAccuracy = 'medium' | 'high'
|
|
2681
|
+
|
|
2682
|
+
export type WhisperPartCallback = (partTranscript: string, partTokenTimeline: Timeline, partWordTimeline: Timeline) => void
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as Onnx from 'onnxruntime-node'
|
|
2
2
|
import { getEmptyRawAudio, RawAudio } from '../audio/AudioUtilities.js'
|
|
3
|
-
import { getWindowWeights,
|
|
3
|
+
import { getWindowWeights, createStftrGenerator, stiftr, WindowType } from '../dsp/FFT.js'
|
|
4
4
|
import { logToStderr } from '../utilities/Utilities.js'
|
|
5
5
|
import { Logger } from '../utilities/Logger.js'
|
|
6
6
|
import { OnnxExecutionProvider, dmlProviderAvailable, getOnnxSessionOptions } from '../utilities/OnnxUtilities.js'
|
|
@@ -74,8 +74,8 @@ export class MDXNet {
|
|
|
74
74
|
const fftSizeReciprocal = 1 / fftSize
|
|
75
75
|
|
|
76
76
|
// Initialize generators for STFT frames for each channel
|
|
77
|
-
const fftFramesLeftGenerator =
|
|
78
|
-
const fftFramesRightGenerator =
|
|
77
|
+
const fftFramesLeftGenerator = await createStftrGenerator(rawAudio.audioChannels[0], fftSize, fftWindowSize, fftHopSize, fftWindowType)
|
|
78
|
+
const fftFramesRightGenerator = await createStftrGenerator(rawAudio.audioChannels[1], fftSize, fftWindowSize, fftHopSize, fftWindowType)
|
|
79
79
|
|
|
80
80
|
// Initial windowed lists to store recently computed STFT frames
|
|
81
81
|
const fftFramesLeftWindowedList = new WindowedList<Float32Array>(segmentSize)
|
|
@@ -95,13 +95,13 @@ export class MDXNet {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
while (fftFramesLeftWindowedList.endOffset < segmentEndFrameOffset) {
|
|
98
|
-
const nextLeftFrameResult =
|
|
98
|
+
const nextLeftFrameResult = fftFramesLeftGenerator.next()
|
|
99
99
|
|
|
100
100
|
if (nextLeftFrameResult.done) {
|
|
101
101
|
break
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
const nextRightFrameResult =
|
|
104
|
+
const nextRightFrameResult = fftFramesRightGenerator.next()
|
|
105
105
|
|
|
106
106
|
if (nextRightFrameResult.done) {
|
|
107
107
|
break
|
|
@@ -2,7 +2,7 @@ import type * as Onnx from 'onnxruntime-node'
|
|
|
2
2
|
import { OnnxExecutionProvider, getOnnxSessionOptions } from '../utilities/OnnxUtilities.js';
|
|
3
3
|
|
|
4
4
|
import { RawAudio } from "../audio/AudioUtilities.js";
|
|
5
|
-
import {
|
|
5
|
+
import { computeMelSpectrogram } from "../dsp/MelSpectrogram.js";
|
|
6
6
|
import { Logger } from '../utilities/Logger.js';
|
|
7
7
|
import { concatFloat32Arrays, splitFloat32Array } from '../utilities/Utilities.js';
|
|
8
8
|
import { applyEmphasis } from '../dsp/MFCC.js';
|
|
@@ -31,7 +31,7 @@ export class Wav2Vec2BertFeatureEmbeddings {
|
|
|
31
31
|
|
|
32
32
|
rawAudio.audioChannels[0] = applyEmphasis(rawAudio.audioChannels[0], 0.97)
|
|
33
33
|
|
|
34
|
-
const {
|
|
34
|
+
const { melSpectrogram } = await computeMelSpectrogram(
|
|
35
35
|
rawAudio,
|
|
36
36
|
512,
|
|
37
37
|
400,
|
|
@@ -42,8 +42,8 @@ export class Wav2Vec2BertFeatureEmbeddings {
|
|
|
42
42
|
'povey')
|
|
43
43
|
|
|
44
44
|
// Ensure even length
|
|
45
|
-
if (
|
|
46
|
-
|
|
45
|
+
if (melSpectrogram.length % 2 != 0) {
|
|
46
|
+
melSpectrogram.push(new Float32Array(80))
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
// Normalize filterbanks
|
|
@@ -51,24 +51,24 @@ export class Wav2Vec2BertFeatureEmbeddings {
|
|
|
51
51
|
let sum = 0
|
|
52
52
|
let sumOfSquares = 0
|
|
53
53
|
|
|
54
|
-
for (let i = 0; i <
|
|
55
|
-
const value =
|
|
54
|
+
for (let i = 0; i < melSpectrogram.length; i++) {
|
|
55
|
+
const value = melSpectrogram[i][filterbankIndex]
|
|
56
56
|
|
|
57
57
|
sum += value
|
|
58
58
|
sumOfSquares += value ** 2
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
const mean = sum /
|
|
62
|
-
const normalizationFactor = 1 / (Math.sqrt(sumOfSquares /
|
|
61
|
+
const mean = sum / melSpectrogram.length
|
|
62
|
+
const normalizationFactor = 1 / (Math.sqrt(sumOfSquares / melSpectrogram.length) + 1e-40)
|
|
63
63
|
|
|
64
|
-
for (let i = 0; i <
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
for (let i = 0; i < melSpectrogram.length; i++) {
|
|
65
|
+
melSpectrogram[i][filterbankIndex] -= mean
|
|
66
|
+
melSpectrogram[i][filterbankIndex] *= normalizationFactor
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// Flatten
|
|
71
|
-
const
|
|
71
|
+
const flattenedMelSpectrogram = concatFloat32Arrays(melSpectrogram)
|
|
72
72
|
|
|
73
73
|
// Initialize session
|
|
74
74
|
await this.initializeSessionIfNeeded()
|
|
@@ -77,9 +77,9 @@ export class Wav2Vec2BertFeatureEmbeddings {
|
|
|
77
77
|
|
|
78
78
|
const Onnx = await import('onnxruntime-node')
|
|
79
79
|
|
|
80
|
-
const inputTensor = new Onnx.Tensor('float32',
|
|
80
|
+
const inputTensor = new Onnx.Tensor('float32', flattenedMelSpectrogram, [1, melSpectrogram.length / 2, 80 * 2])
|
|
81
81
|
|
|
82
|
-
const attentionMask = new Int32Array(
|
|
82
|
+
const attentionMask = new Int32Array(melSpectrogram.length / 2).fill(1)
|
|
83
83
|
const attentionMaskTensor = new Onnx.Tensor('int32', attentionMask, [1, attentionMask.length])
|
|
84
84
|
|
|
85
85
|
// Run inference
|
|
@@ -130,13 +130,15 @@ export function sleep(timeMs: number) {
|
|
|
130
130
|
return new Promise<void>((resolve) => {
|
|
131
131
|
const tickCallback = () => {
|
|
132
132
|
if (timer.elapsedTime < timeMs) {
|
|
133
|
-
setImmediate(tickCallback)
|
|
133
|
+
//setImmediate(tickCallback)
|
|
134
|
+
setTimeout(tickCallback, 0)
|
|
134
135
|
} else {
|
|
135
136
|
resolve()
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
139
|
|
|
139
|
-
setImmediate(tickCallback)
|
|
140
|
+
//setImmediate(tickCallback)
|
|
141
|
+
setTimeout(tickCallback, 0)
|
|
140
142
|
})
|
|
141
143
|
}
|
|
142
144
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MelSpectogram.js","sourceRoot":"","sources":["../../src/dsp/MelSpectogram.ts"],"names":[],"mappings":"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"}
|