echogarden 1.6.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/schemas/options.json +50 -12
- package/dist/api/Alignment.js +2 -2
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/Denoising.js +5 -4
- package/dist/api/Denoising.js.map +1 -1
- package/dist/api/Recognition.js +2 -2
- package/dist/api/Recognition.js.map +1 -1
- package/dist/api/SourceSeparation.d.ts +2 -5
- package/dist/api/SourceSeparation.js +14 -13
- package/dist/api/SourceSeparation.js.map +1 -1
- package/dist/api/SpeechLanguageDetection.js +2 -2
- package/dist/api/SpeechLanguageDetection.js.map +1 -1
- package/dist/api/SpeechTranslation.js +2 -2
- package/dist/api/SpeechTranslation.js.map +1 -1
- package/dist/api/Synthesis.js +8 -6
- package/dist/api/Synthesis.js.map +1 -1
- package/dist/api/TextTranslation.d.ts +3 -1
- package/dist/api/TextTranslation.js +12 -6
- package/dist/api/TextTranslation.js.map +1 -1
- package/dist/api/TranslationAlignment.js +2 -2
- package/dist/api/TranslationAlignment.js.map +1 -1
- package/dist/audio/AudioUtilities.d.ts +5 -3
- package/dist/audio/AudioUtilities.js +39 -25
- package/dist/audio/AudioUtilities.js.map +1 -1
- package/dist/build-tools/MakeTarballsForInstalledPackages.d.ts +1 -0
- package/dist/build-tools/MakeTarballsForInstalledPackages.js +20 -0
- package/dist/build-tools/MakeTarballsForInstalledPackages.js.map +1 -0
- package/dist/cli/CLI.js +4 -3
- package/dist/cli/CLI.js.map +1 -1
- package/dist/dsp/FFT.d.ts +3 -3
- package/dist/dsp/FFT.js +55 -34
- package/dist/dsp/FFT.js.map +1 -1
- package/dist/nlp/IPA.d.ts +3 -3
- package/dist/nlp/Segmentation.js +2 -1
- package/dist/nlp/Segmentation.js.map +1 -1
- package/dist/nlp/TextNormalizer.d.ts +2 -0
- package/dist/nlp/TextNormalizer.js +51 -0
- package/dist/nlp/TextNormalizer.js.map +1 -1
- package/dist/recognition/WhisperSTT.d.ts +5 -0
- package/dist/recognition/WhisperSTT.js +19 -11
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/source-separation/MDXNetSourceSeparation.d.ts +26 -3
- package/dist/source-separation/MDXNetSourceSeparation.js +159 -79
- package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
- package/dist/subtitles/Subtitles.js +2 -1
- package/dist/subtitles/Subtitles.js.map +1 -1
- package/dist/synthesis/EspeakTTS.js +5 -5
- package/dist/synthesis/EspeakTTS.js.map +1 -1
- package/dist/tests/Test.js +0 -1
- package/dist/tests/Test.js.map +1 -1
- package/dist/text-translation/GoogleTranslateTextTranslation.d.ts +9 -3
- package/dist/text-translation/GoogleTranslateTextTranslation.js +59 -29
- package/dist/text-translation/GoogleTranslateTextTranslation.js.map +1 -1
- package/dist/utilities/OnnxUtilities.d.ts +1 -0
- package/dist/utilities/OnnxUtilities.js +5 -5
- package/dist/utilities/OnnxUtilities.js.map +1 -1
- package/dist/utilities/PackageManager.js +6 -6
- package/dist/utilities/PackageManager.js.map +1 -1
- package/dist/utilities/StringUtilities.d.ts +28 -0
- package/dist/utilities/StringUtilities.js +150 -0
- package/dist/utilities/StringUtilities.js.map +1 -0
- package/dist/utilities/TarballMaker.d.ts +3 -4
- package/dist/utilities/TarballMaker.js +6 -19
- package/dist/utilities/TarballMaker.js.map +1 -1
- package/dist/utilities/Timeline.js +2 -1
- package/dist/utilities/Timeline.js.map +1 -1
- package/dist/utilities/Utilities.d.ts +1 -33
- package/dist/utilities/Utilities.js +3 -176
- package/dist/utilities/Utilities.js.map +1 -1
- package/dist/utilities/WasmMemoryManager.d.ts +9 -1
- package/dist/utilities/WasmMemoryManager.js +24 -3
- package/dist/utilities/WasmMemoryManager.js.map +1 -1
- package/dist/utilities/WebReader.js +1 -1
- package/dist/utilities/WebReader.js.map +1 -1
- package/dist/utilities/WikipediaReader.js +1 -1
- package/dist/utilities/WikipediaReader.js.map +1 -1
- package/dist/utilities/WindowedList.d.ts +10 -0
- package/dist/utilities/WindowedList.js +36 -0
- package/dist/utilities/WindowedList.js.map +1 -0
- package/docs/Engines.md +1 -1
- package/docs/Options.md +16 -10
- package/package.json +11 -11
- package/src/alignment/SpeechAlignment.ts +1 -1
- package/src/api/Alignment.ts +2 -2
- package/src/api/Denoising.ts +5 -4
- package/src/api/Recognition.ts +3 -3
- package/src/api/SourceSeparation.ts +20 -19
- package/src/api/SpeechLanguageDetection.ts +2 -2
- package/src/api/SpeechTranslation.ts +2 -2
- package/src/api/Synthesis.ts +8 -6
- package/src/api/TextTranslation.ts +17 -9
- package/src/api/TranslationAlignment.ts +2 -2
- package/src/audio/AudioUtilities.ts +53 -33
- package/src/build-tools/MakeTarballsForInstalledPackages.ts +25 -0
- package/src/cli/CLI.ts +4 -3
- package/src/dsp/FFT.ts +65 -42
- package/src/nlp/Segmentation.ts +2 -1
- package/src/nlp/TextNormalizer.ts +60 -0
- package/src/recognition/WhisperSTT.ts +25 -12
- package/src/source-separation/MDXNetSourceSeparation.ts +218 -88
- package/src/subtitles/Subtitles.ts +2 -1
- package/src/synthesis/EspeakTTS.ts +5 -5
- package/src/tests/Test.ts +0 -4
- package/src/text-translation/GoogleTranslateTextTranslation.ts +91 -30
- package/src/typings/Fillers.d.ts +2 -0
- package/src/utilities/OnnxUtilities.ts +7 -8
- package/src/utilities/PackageManager.ts +6 -7
- package/src/utilities/StringUtilities.ts +203 -0
- package/src/utilities/TarballMaker.ts +6 -23
- package/src/utilities/Timeline.ts +2 -1
- package/src/utilities/Utilities.ts +4 -237
- package/src/utilities/WasmMemoryManager.ts +38 -4
- package/src/utilities/WebReader.ts +1 -1
- package/src/utilities/WikipediaReader.ts +1 -1
- package/src/utilities/WindowedList.ts +45 -0
|
@@ -47,11 +47,13 @@ export function trimAudioEnd(audioSamples: Float32Array, targetEndSilentSampleCo
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export function getStartingSilentSampleCount(audioSamples: Float32Array, amplitudeThresholdDecibels = defaultSilenceThresholdDecibels) {
|
|
50
|
+
const sampleCount = audioSamples.length
|
|
51
|
+
|
|
50
52
|
const minSampleAmplitude = decibelsToGainFactor(amplitudeThresholdDecibels)
|
|
51
53
|
|
|
52
54
|
let silentSampleCount = 0
|
|
53
55
|
|
|
54
|
-
for (let i = 0; i <
|
|
56
|
+
for (let i = 0; i < sampleCount - 1; i++) {
|
|
55
57
|
if (Math.abs(audioSamples[i]) > minSampleAmplitude) {
|
|
56
58
|
break
|
|
57
59
|
}
|
|
@@ -63,11 +65,13 @@ export function getStartingSilentSampleCount(audioSamples: Float32Array, amplitu
|
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
export function getEndingSilentSampleCount(audioSamples: Float32Array, amplitudeThresholdDecibels = defaultSilenceThresholdDecibels) {
|
|
68
|
+
const sampleCount = audioSamples.length
|
|
69
|
+
|
|
66
70
|
const minSampleAmplitude = decibelsToGainFactor(amplitudeThresholdDecibels)
|
|
67
71
|
|
|
68
72
|
let silentSampleCount = 0
|
|
69
73
|
|
|
70
|
-
for (let i =
|
|
74
|
+
for (let i = sampleCount - 1; i >= 0; i--) {
|
|
71
75
|
if (Math.abs(audioSamples[i]) > minSampleAmplitude) {
|
|
72
76
|
break
|
|
73
77
|
}
|
|
@@ -82,15 +86,27 @@ export function getEndingSilentSampleCount(audioSamples: Float32Array, amplitude
|
|
|
82
86
|
// Gain, normalization, mixing, and channel downmixing
|
|
83
87
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
84
88
|
export function downmixToMonoAndNormalize(rawAudio: RawAudio, targetPeakDecibels = -3) {
|
|
85
|
-
|
|
89
|
+
const downmixedAudio = downmixToMono(rawAudio)
|
|
90
|
+
|
|
91
|
+
normalizeAudioLevelInPlace(downmixedAudio, targetPeakDecibels)
|
|
92
|
+
|
|
93
|
+
return downmixedAudio
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function attenuateIfClippingInPlace(rawAudio: RawAudio, clippingThreshold = -0.1) {
|
|
97
|
+
normalizeAudioLevelInPlace(rawAudio, clippingThreshold, 0)
|
|
86
98
|
}
|
|
87
99
|
|
|
88
|
-
export function
|
|
89
|
-
|
|
100
|
+
export function normalizeAudioLevel(rawAudio: RawAudio, targetPeakDecibels = -3, maxGainIncreaseDecibels = 30) {
|
|
101
|
+
const clonedRawAudio = cloneRawAudio(rawAudio)
|
|
102
|
+
|
|
103
|
+
normalizeAudioLevelInPlace(clonedRawAudio, targetPeakDecibels, maxGainIncreaseDecibels)
|
|
104
|
+
|
|
105
|
+
return clonedRawAudio
|
|
90
106
|
}
|
|
91
107
|
|
|
92
|
-
export function
|
|
93
|
-
//
|
|
108
|
+
export function normalizeAudioLevelInPlace(rawAudio: RawAudio, targetPeakDecibels = -3, maxGainIncreaseDecibels = 30){
|
|
109
|
+
//correctDCBiasInPlace(rawAudio)
|
|
94
110
|
|
|
95
111
|
const targetPeakAmplitude = decibelsToGainFactor(targetPeakDecibels)
|
|
96
112
|
const maxGainFactor = decibelsToGainFactor(maxGainIncreaseDecibels)
|
|
@@ -99,12 +115,10 @@ export function normalizeAudioLevel(rawAudio: RawAudio, targetPeakDecibels = -3,
|
|
|
99
115
|
|
|
100
116
|
const gainFactor = Math.min(targetPeakAmplitude / peakAmplitude, maxGainFactor)
|
|
101
117
|
|
|
102
|
-
|
|
118
|
+
applyGainFactorInPlace(rawAudio, gainFactor)
|
|
103
119
|
}
|
|
104
120
|
|
|
105
|
-
export function
|
|
106
|
-
const outputAudioChannels: Float32Array[] = []
|
|
107
|
-
|
|
121
|
+
export function correctDCBiasInPlace(rawAudio: RawAudio) {
|
|
108
122
|
for (const channelSamples of rawAudio.audioChannels) {
|
|
109
123
|
const sampleCount = channelSamples.length
|
|
110
124
|
|
|
@@ -116,38 +130,36 @@ export function correctDCBias(rawAudio: RawAudio): RawAudio {
|
|
|
116
130
|
|
|
117
131
|
const sampleAverage = sampleSum / sampleCount
|
|
118
132
|
|
|
119
|
-
const outputChannelSamples = new Float32Array(sampleCount)
|
|
120
|
-
|
|
121
133
|
for (let i = 0; i < sampleCount; i++) {
|
|
122
|
-
|
|
134
|
+
channelSamples[i] -= sampleAverage
|
|
123
135
|
}
|
|
124
|
-
|
|
125
|
-
outputAudioChannels.push(outputChannelSamples)
|
|
126
136
|
}
|
|
137
|
+
}
|
|
127
138
|
|
|
128
|
-
|
|
139
|
+
export function applyGainDecibels(rawAudio: RawAudio, gainDecibels: number) {
|
|
140
|
+
const clonedRawAudio = cloneRawAudio(rawAudio)
|
|
141
|
+
|
|
142
|
+
applyGainDecibelsInPlace(clonedRawAudio, gainDecibels)
|
|
143
|
+
|
|
144
|
+
return clonedRawAudio
|
|
129
145
|
}
|
|
130
146
|
|
|
131
|
-
export function
|
|
132
|
-
|
|
147
|
+
export function applyGainDecibelsInPlace(rawAudio: RawAudio, gainDecibels: number) {
|
|
148
|
+
applyGainFactorInPlace(rawAudio, decibelsToGainFactor(gainDecibels))
|
|
133
149
|
}
|
|
134
150
|
|
|
135
|
-
export function
|
|
136
|
-
|
|
151
|
+
export function applyGainFactorInPlace(rawAudio: RawAudio, gainFactor: number) {
|
|
152
|
+
if (gainFactor === 1.0) {
|
|
153
|
+
return
|
|
154
|
+
}
|
|
137
155
|
|
|
138
156
|
for (const channelSamples of rawAudio.audioChannels) {
|
|
139
157
|
const sampleCount = channelSamples.length
|
|
140
158
|
|
|
141
|
-
const outputChannelSamples = new Float32Array(sampleCount)
|
|
142
|
-
|
|
143
159
|
for (let i = 0; i < sampleCount; i++) {
|
|
144
|
-
|
|
160
|
+
channelSamples[i] *= gainFactor
|
|
145
161
|
}
|
|
146
|
-
|
|
147
|
-
outputAudioChannels.push(outputChannelSamples)
|
|
148
162
|
}
|
|
149
|
-
|
|
150
|
-
return { audioChannels: outputAudioChannels, sampleRate: rawAudio.sampleRate } as RawAudio
|
|
151
163
|
}
|
|
152
164
|
|
|
153
165
|
export function downmixToMono(rawAudio: RawAudio): RawAudio {
|
|
@@ -183,8 +195,14 @@ export function getSamplePeakAmplitude(audioChannels: Float32Array[]) {
|
|
|
183
195
|
let maxAmplitude = 0.00001
|
|
184
196
|
|
|
185
197
|
for (const channelSamples of audioChannels) {
|
|
186
|
-
|
|
187
|
-
|
|
198
|
+
const sampleCount = channelSamples.length
|
|
199
|
+
|
|
200
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
201
|
+
const sampleAbsValue = Math.abs(channelSamples[i])
|
|
202
|
+
|
|
203
|
+
if (sampleAbsValue > maxAmplitude) {
|
|
204
|
+
maxAmplitude = sampleAbsValue
|
|
205
|
+
}
|
|
188
206
|
}
|
|
189
207
|
}
|
|
190
208
|
|
|
@@ -257,7 +275,7 @@ export function sliceAudioChannels(audioChannels: Float32Array[], startSampleInd
|
|
|
257
275
|
}
|
|
258
276
|
|
|
259
277
|
export function concatAudioSegments(audioSegments: Float32Array[][]) {
|
|
260
|
-
if (audioSegments.length
|
|
278
|
+
if (audioSegments.length === 0) {
|
|
261
279
|
return []
|
|
262
280
|
}
|
|
263
281
|
|
|
@@ -317,7 +335,9 @@ export function fadeAudioInOut(rawAudio: RawAudio, fadeTime: number): RawAudio {
|
|
|
317
335
|
const outAudioChannels = rawAudio.audioChannels.map(channel => channel.slice())
|
|
318
336
|
|
|
319
337
|
for (const channel of outAudioChannels) {
|
|
320
|
-
|
|
338
|
+
const sampleCount = channel.length
|
|
339
|
+
|
|
340
|
+
if (sampleCount < fadeSampleCount * 2) {
|
|
321
341
|
continue
|
|
322
342
|
}
|
|
323
343
|
|
|
@@ -331,7 +351,7 @@ export function fadeAudioInOut(rawAudio: RawAudio, fadeTime: number): RawAudio {
|
|
|
331
351
|
|
|
332
352
|
factor = 1.0
|
|
333
353
|
|
|
334
|
-
for (let i =
|
|
354
|
+
for (let i = sampleCount - fadeSampleCount; i < sampleCount; i++) {
|
|
335
355
|
channel[i] *= factor
|
|
336
356
|
|
|
337
357
|
factor *= gainReductionPerFrameFactor
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { existsSync, readdir } from '../utilities/FileSystem.js'
|
|
3
|
+
import { ensureAndGetPackagesDir } from '../utilities/PackageManager.js'
|
|
4
|
+
import { createNamedTarball } from '../utilities/TarballMaker.js'
|
|
5
|
+
import { setupProgramTerminationListeners } from '../utilities/Utilities.js'
|
|
6
|
+
|
|
7
|
+
async function makeTarballsForInstalledPackages(tarballDir: string, skipIfExists = false) {
|
|
8
|
+
const packagesDir = await ensureAndGetPackagesDir()
|
|
9
|
+
const packageList = await readdir(packagesDir)
|
|
10
|
+
|
|
11
|
+
for (const packageName of packageList) {
|
|
12
|
+
if (skipIfExists && existsSync(path.join(tarballDir, `${packageName}.tar.gz`))) {
|
|
13
|
+
continue
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const packagePath = path.join(packagesDir, packageName)
|
|
17
|
+
await createNamedTarball(packagePath, packageName, tarballDir)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setupProgramTerminationListeners()
|
|
22
|
+
|
|
23
|
+
await makeTarballsForInstalledPackages('../resources/tarballs', true)
|
|
24
|
+
|
|
25
|
+
process.exit(0)
|
package/src/cli/CLI.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as API from '../api/API.js'
|
|
2
2
|
import { parseCLIArguments } from './CLIParser.js'
|
|
3
|
-
import {
|
|
3
|
+
import { getWithDefault, logToStderr, setupUnhandledExceptionListeners, splitFilenameOnExtendedExtension, stringifyAndFormatJson } from '../utilities/Utilities.js'
|
|
4
4
|
import { getOptionTypeFromSchema, SchemaTypeDefinition } from './CLIOptionsSchema.js'
|
|
5
5
|
import { ParsedConfigFile, parseConfigFile, parseJSONConfigFile } from './CLIConfigFile.js'
|
|
6
6
|
|
|
@@ -26,6 +26,7 @@ import { OpenPromise } from '../utilities/OpenPromise.js'
|
|
|
26
26
|
import JSON5 from 'json5'
|
|
27
27
|
import { getLowercaseFileExtension, resolveToModuleRootDir } from '../utilities/PathUtilities.js'
|
|
28
28
|
import { CLIOptions, CLIOptionsKeys } from './CLIOptions.js'
|
|
29
|
+
import { convertHtmlToText, formatIntegerWithLeadingZeros, formatListWithQuotedElements } from '../utilities/StringUtilities.js'
|
|
29
30
|
|
|
30
31
|
//const log = logToStderr
|
|
31
32
|
|
|
@@ -1049,7 +1050,7 @@ export async function translateText(operationData: CLIOperationData) {
|
|
|
1049
1050
|
} else if (inputFileExtension == 'srt' || inputFileExtension == 'vtt') {
|
|
1050
1051
|
inputText = subtitlesToText(inputFileContent)
|
|
1051
1052
|
} else {
|
|
1052
|
-
throw new Error(`
|
|
1053
|
+
throw new Error(`translate-text only supports input files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`)
|
|
1053
1054
|
}
|
|
1054
1055
|
|
|
1055
1056
|
const options = await optionsLookupToTypedObject(operationOptionsLookup, 'TextTranslationOptions')
|
|
@@ -1066,7 +1067,7 @@ export async function translateText(operationData: CLIOperationData) {
|
|
|
1066
1067
|
|
|
1067
1068
|
sourceLanguage,
|
|
1068
1069
|
targetLanguage,
|
|
1069
|
-
} = await API.translateText(
|
|
1070
|
+
} = await API.translateText(inputText, options)
|
|
1070
1071
|
|
|
1071
1072
|
if (outputFilenames.length > 0) {
|
|
1072
1073
|
logger.start('\nWrite output files')
|
package/src/dsp/FFT.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ComplexNumber, sumVector } from '../math/VectorMath.js'
|
|
1
|
+
import { ComplexNumber } from '../math/VectorMath.js'
|
|
3
2
|
import { concatFloat32Arrays } from '../utilities/Utilities.js'
|
|
4
3
|
import { WasmMemoryManager } from '../utilities/WasmMemoryManager.js'
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
// Compute short-term Fourier transform (real-valued)
|
|
8
6
|
export async function stftr(samples: Float32Array, fftOrder: number, windowSize: number, hopSize: number, windowType: WindowType) {
|
|
7
|
+
const frames: Float32Array[] = []
|
|
8
|
+
|
|
9
|
+
for await (const frame of stftrGenerator(samples, fftOrder, windowSize, hopSize, windowType)) {
|
|
10
|
+
frames.push(frame)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return frames
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Incrementally generate short-term Fourier transform frames (real-valued)
|
|
17
|
+
export async function* stftrGenerator(samples: Float32Array, fftOrder: number, windowSize: number, hopSize: number, windowType: WindowType) {
|
|
9
18
|
if (fftOrder % 2 != 0 || windowSize % 2 != 0) {
|
|
10
|
-
throw new Error('FFT order and window size must multiples of 2')
|
|
19
|
+
throw new Error('FFT order and window size must be multiples of 2')
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
if (windowSize > fftOrder) {
|
|
@@ -25,17 +34,18 @@ export async function stftr(samples: Float32Array, fftOrder: number, windowSize:
|
|
|
25
34
|
|
|
26
35
|
const windowWeights = getWindowWeights(windowType, windowSize)
|
|
27
36
|
|
|
28
|
-
const m = await
|
|
29
|
-
const wasmMemory = new WasmMemoryManager(m
|
|
37
|
+
const m = await getPFFFTInstance(isPffftSimdSupportedForFFTOrder(fftOrder))
|
|
38
|
+
const wasmMemory = new WasmMemoryManager(m, {
|
|
39
|
+
wasmAlloc: m._pffft_aligned_malloc,
|
|
40
|
+
wasmFree: m._pffft_aligned_free
|
|
41
|
+
})
|
|
30
42
|
|
|
31
|
-
const statePtr = m.
|
|
32
|
-
wasmMemory.wrapPointer(statePtr)
|
|
43
|
+
const statePtr = m._pffft_new_setup(fftOrder, 0)
|
|
33
44
|
|
|
34
45
|
const sampleCount = samples.length
|
|
35
46
|
const frameBufferRef = wasmMemory.allocFloat32Array(fftOrder)
|
|
36
47
|
const binsBufferRef = wasmMemory.allocFloat32Array(fftOrder * 2)
|
|
37
|
-
|
|
38
|
-
const frames: Float32Array[] = []
|
|
48
|
+
const workBufferRef = wasmMemory.allocFloat32Array(fftOrder * 2)
|
|
39
49
|
|
|
40
50
|
for (let offset = 0; offset < sampleCount; offset += hopSize) {
|
|
41
51
|
const windowSamples = samples.subarray(offset, offset + windowSize)
|
|
@@ -49,17 +59,19 @@ export async function stftr(samples: Float32Array, fftOrder: number, windowSize:
|
|
|
49
59
|
|
|
50
60
|
binsBufferRef.clear()
|
|
51
61
|
|
|
52
|
-
m.
|
|
62
|
+
m._pffft_transform_ordered(statePtr, frameBufferRef.address, binsBufferRef.address, workBufferRef.address, 0)
|
|
53
63
|
|
|
54
64
|
const bins = binsBufferRef.view.slice(0, fftOrder + 2)
|
|
55
|
-
|
|
65
|
+
|
|
66
|
+
yield bins
|
|
56
67
|
}
|
|
57
68
|
|
|
58
|
-
|
|
69
|
+
m._pffft_destroy_setup(statePtr)
|
|
59
70
|
|
|
60
|
-
|
|
71
|
+
wasmMemory.freeAll()
|
|
61
72
|
}
|
|
62
73
|
|
|
74
|
+
// Compute short-term inverse Fourier transform (real-valued)
|
|
63
75
|
export async function stiftr(binsForFrames: Float32Array[], fftOrder: number, windowSize: number, hopSize: number, windowType: WindowType, expectedOutputLength?: number) {
|
|
64
76
|
if (fftOrder % 2 != 0 || windowSize % 2 != 0) {
|
|
65
77
|
throw new Error('FFT order and window size must multiples of 2')
|
|
@@ -87,14 +99,18 @@ export async function stiftr(binsForFrames: Float32Array[], fftOrder: number, wi
|
|
|
87
99
|
|
|
88
100
|
const outSamples = new Float32Array(outSampleCount)
|
|
89
101
|
|
|
90
|
-
const m = await
|
|
91
|
-
|
|
102
|
+
const m = await getPFFFTInstance(isPffftSimdSupportedForFFTOrder(fftOrder))
|
|
103
|
+
|
|
104
|
+
const wasmMemory = new WasmMemoryManager(m, {
|
|
105
|
+
wasmAlloc: m._pffft_aligned_malloc,
|
|
106
|
+
wasmFree: m._pffft_aligned_free
|
|
107
|
+
})
|
|
92
108
|
|
|
93
|
-
const statePtr = m.
|
|
94
|
-
wasmMemory.wrapPointer(statePtr)
|
|
109
|
+
const statePtr = m._pffft_new_setup(fftOrder, 0)
|
|
95
110
|
|
|
96
111
|
const frameBufferRef = wasmMemory.allocFloat32Array(fftOrder)
|
|
97
112
|
const binsRef = wasmMemory.allocFloat32Array(fftOrder * 2)
|
|
113
|
+
const workBufferRef = wasmMemory.allocFloat32Array(fftOrder * 2)
|
|
98
114
|
|
|
99
115
|
const sumOfSquaredWeightsForSample = new Float32Array(outSampleCount)
|
|
100
116
|
|
|
@@ -105,7 +121,7 @@ export async function stiftr(binsForFrames: Float32Array[], fftOrder: number, wi
|
|
|
105
121
|
|
|
106
122
|
frameBufferRef.clear()
|
|
107
123
|
|
|
108
|
-
m.
|
|
124
|
+
m._pffft_transform_ordered(statePtr, binsRef.address, frameBufferRef.address, workBufferRef.address, 1)
|
|
109
125
|
|
|
110
126
|
const frameSamples = frameBufferRef.view
|
|
111
127
|
|
|
@@ -123,6 +139,7 @@ export async function stiftr(binsForFrames: Float32Array[], fftOrder: number, wi
|
|
|
123
139
|
}
|
|
124
140
|
}
|
|
125
141
|
|
|
142
|
+
m._pffft_destroy_setup(statePtr)
|
|
126
143
|
wasmMemory.freeAll()
|
|
127
144
|
|
|
128
145
|
// Divide each output sample by the sum of squared weights
|
|
@@ -135,6 +152,7 @@ export async function stiftr(binsForFrames: Float32Array[], fftOrder: number, wi
|
|
|
135
152
|
return outSamplesTrimmed
|
|
136
153
|
}
|
|
137
154
|
|
|
155
|
+
// Get bin frequency thresholds for a particular bin count and maximum frequency
|
|
138
156
|
export function getBinFrequencies(binCount: number, maxFrequency: number) {
|
|
139
157
|
const binFrequencies = new Float32Array(binCount)
|
|
140
158
|
const frequencyStep = maxFrequency / (binCount - 1)
|
|
@@ -146,10 +164,12 @@ export function getBinFrequencies(binCount: number, maxFrequency: number) {
|
|
|
146
164
|
return binFrequencies
|
|
147
165
|
}
|
|
148
166
|
|
|
167
|
+
// Convert an array of raw FFT frames to a power spectrum
|
|
149
168
|
export function fftFramesToPowerSpectogram(fftFrames: Float32Array[]) {
|
|
150
169
|
return fftFrames.map(fftFrame => fftFrameToPowerSpectrum(fftFrame))
|
|
151
170
|
}
|
|
152
171
|
|
|
172
|
+
// Convert a raw FFT frame to a power spectrum
|
|
153
173
|
export function fftFrameToPowerSpectrum(fftFrame: Float32Array) {
|
|
154
174
|
const powerSpectrum = new Float32Array(fftFrame.length / 2)
|
|
155
175
|
|
|
@@ -165,6 +185,7 @@ export function fftFrameToPowerSpectrum(fftFrame: Float32Array) {
|
|
|
165
185
|
return powerSpectrum
|
|
166
186
|
}
|
|
167
187
|
|
|
188
|
+
// Convert raw FFT frames to an array of complex numbers
|
|
168
189
|
export function binBufferToComplex(bins: Float32Array, extendAndMirror = false) {
|
|
169
190
|
const complexBins: ComplexNumber[] = []
|
|
170
191
|
|
|
@@ -191,6 +212,7 @@ export function binBufferToComplex(bins: Float32Array, extendAndMirror = false)
|
|
|
191
212
|
return complexBins
|
|
192
213
|
}
|
|
193
214
|
|
|
215
|
+
// Convert an array of complex numbers to raw FFT frames
|
|
194
216
|
export function complexToBinBuffer(complexBins: ComplexNumber[]) {
|
|
195
217
|
const binBuffer = new Float32Array(complexBins.length * 2)
|
|
196
218
|
|
|
@@ -204,16 +226,7 @@ export function complexToBinBuffer(complexBins: ComplexNumber[]) {
|
|
|
204
226
|
return binBuffer
|
|
205
227
|
}
|
|
206
228
|
|
|
207
|
-
|
|
208
|
-
if (!kissFFTInstance) {
|
|
209
|
-
const { default: initializer } = await import('@echogarden/kissfft-wasm')
|
|
210
|
-
|
|
211
|
-
kissFFTInstance = await initializer()
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return kissFFTInstance
|
|
215
|
-
}
|
|
216
|
-
|
|
229
|
+
// Get window weights for a particular window function
|
|
217
230
|
export function getWindowWeights(windowType: WindowType, windowSize: number) {
|
|
218
231
|
const weights = new Float32Array(windowSize)
|
|
219
232
|
|
|
@@ -242,23 +255,33 @@ export function getWindowWeights(windowType: WindowType, windowSize: number) {
|
|
|
242
255
|
return weights
|
|
243
256
|
}
|
|
244
257
|
|
|
245
|
-
export
|
|
246
|
-
|
|
258
|
+
export function isPffftSimdSupportedForFFTOrder(fftOrder: number) {
|
|
259
|
+
return fftOrder % 32 === 0
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
let pffftNonSimdInstance: any
|
|
263
|
+
let pffftSimdInstance: any
|
|
264
|
+
|
|
265
|
+
export async function getPFFFTInstance(enableSimd: boolean) {
|
|
266
|
+
return enableSimd ? getSimdPFFFTInstance() : getNonSimdPFFFTInstance()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Get non-SIMD PFFFT instance (initialize new if not exists)
|
|
270
|
+
async function getNonSimdPFFFTInstance() {
|
|
271
|
+
const { default: initializer } = await import('@echogarden/pffft-wasm')
|
|
247
272
|
|
|
248
|
-
|
|
273
|
+
pffftNonSimdInstance = await initializer()
|
|
249
274
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const hopLength = windowSize / 2
|
|
253
|
-
const windowType: WindowType = 'hann'
|
|
275
|
+
return pffftNonSimdInstance
|
|
276
|
+
}
|
|
254
277
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const
|
|
278
|
+
// Get SIMD PFFFT instance (initialize new if not exists)
|
|
279
|
+
async function getSimdPFFFTInstance() {
|
|
280
|
+
const { default: initializer } = await import('@echogarden/pffft-wasm/simd')
|
|
258
281
|
|
|
259
|
-
|
|
282
|
+
pffftSimdInstance = await initializer()
|
|
260
283
|
|
|
261
|
-
return
|
|
284
|
+
return pffftSimdInstance
|
|
262
285
|
}
|
|
263
286
|
|
|
264
287
|
export type WindowType = 'hann' | 'hamming' | 'povey'
|
package/src/nlp/Segmentation.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as CldrSegmentation from 'cldr-segmentation'
|
|
2
2
|
import { splitChineseTextToWords_Jieba } from './ChineseSegmentation.js'
|
|
3
3
|
|
|
4
|
-
import { sumArray,
|
|
4
|
+
import { sumArray, logToStderr } from '../utilities/Utilities.js'
|
|
5
5
|
import { getShortLanguageCode } from '../utilities/Locale.js'
|
|
6
6
|
import { splitJapaneseTextToWords_Kuromoji } from './JapaneseSegmentation.js'
|
|
7
7
|
import { ParagraphBreakType, WhitespaceProcessing } from '../api/Common.js'
|
|
8
|
+
import { includesAnyOf, indexOfAnyOf } from '../utilities/StringUtilities.js'
|
|
8
9
|
|
|
9
10
|
const log = logToStderr
|
|
10
11
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getShortLanguageCode } from '../utilities/Locale.js'
|
|
2
|
+
import { substituteCharactersUsingLookup } from '../utilities/StringUtilities.js'
|
|
2
3
|
|
|
3
4
|
export function getNormalizedFragmentsForSpeech(words: string[], language: string) {
|
|
4
5
|
language = getShortLanguageCode(language)
|
|
@@ -158,3 +159,62 @@ export function normalizeFourDigitDecadeString(decadeString: string) {
|
|
|
158
159
|
|
|
159
160
|
return normalizedString
|
|
160
161
|
}
|
|
162
|
+
|
|
163
|
+
export function simplifyPunctuationCharacters(text: string) {
|
|
164
|
+
return substituteCharactersUsingLookup(text, punctuationSubstitutionLookup)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export const punctuationSubstitutionLookup: Record<string, string> = {
|
|
168
|
+
'“': `"`,
|
|
169
|
+
'”': `"`,
|
|
170
|
+
'‟': `"`,
|
|
171
|
+
'ˮ': `"`,
|
|
172
|
+
'„': `"`,
|
|
173
|
+
'‹': `"`,
|
|
174
|
+
'›': `"`,
|
|
175
|
+
'❮': `"`,
|
|
176
|
+
'❯': '"',
|
|
177
|
+
'«': `"`,
|
|
178
|
+
'»': `"`,
|
|
179
|
+
'״': `"`,
|
|
180
|
+
'❝': `"`,
|
|
181
|
+
'❞': `"`,
|
|
182
|
+
'🙶': `"`,
|
|
183
|
+
'🙷': `"`,
|
|
184
|
+
'⹂': `"`,
|
|
185
|
+
'〝': `"`,
|
|
186
|
+
'〞': `"`,
|
|
187
|
+
'〟': `"`,
|
|
188
|
+
'"': `"`,
|
|
189
|
+
'❠': `"`,
|
|
190
|
+
'🙸': `"`,
|
|
191
|
+
|
|
192
|
+
'ߵ': `'`,
|
|
193
|
+
'ߴ': `'`,
|
|
194
|
+
'’': `'`,
|
|
195
|
+
'‘': `'`,
|
|
196
|
+
'ʹ': `'`,
|
|
197
|
+
'ʼ': `'`,
|
|
198
|
+
''': `'`,
|
|
199
|
+
'ʻ': `'`,
|
|
200
|
+
'՚': `'`,
|
|
201
|
+
'՛': `'`,
|
|
202
|
+
'❛': `'`,
|
|
203
|
+
'❜': `'`,
|
|
204
|
+
'❟': `'`,
|
|
205
|
+
|
|
206
|
+
',': `,`,
|
|
207
|
+
'、': `,`,
|
|
208
|
+
|
|
209
|
+
':': `:`,
|
|
210
|
+
|
|
211
|
+
';': `;`,
|
|
212
|
+
|
|
213
|
+
'。': `.`,
|
|
214
|
+
|
|
215
|
+
'?': `?`,
|
|
216
|
+
'؟': `?`,
|
|
217
|
+
|
|
218
|
+
'!': `!`,
|
|
219
|
+
'¡': `!`,
|
|
220
|
+
}
|
|
@@ -2,7 +2,7 @@ import type * as Onnx from 'onnxruntime-node'
|
|
|
2
2
|
|
|
3
3
|
import { Logger } from '../utilities/Logger.js'
|
|
4
4
|
import { computeMelSpectogramUsingFilterbanks, Filterbank } from '../dsp/MelSpectogram.js'
|
|
5
|
-
import { clip,
|
|
5
|
+
import { clip, getIntegerRange, splitFloat32Array, yieldToEventLoop } from '../utilities/Utilities.js'
|
|
6
6
|
import { indexOfMax, logOfVector, logSumExp, meanOfVector, softmax, stdDeviationOfVector } from '../math/VectorMath.js'
|
|
7
7
|
|
|
8
8
|
import { alignDTWWindowed } from '../alignment/DTWSequenceAlignmentWindowed.js'
|
|
@@ -22,8 +22,9 @@ import { type Tiktoken } from 'tiktoken/lite'
|
|
|
22
22
|
import { isPunctuation, isWhitespace, isWord, splitToSentences, splitToWords } from '../nlp/Segmentation.js'
|
|
23
23
|
import { medianOf5Filter } from '../math/MedianFilter.js'
|
|
24
24
|
import { getDeflateCompressionMetricsForString } from '../utilities/Compression.js'
|
|
25
|
-
import { getOnnxSessionOptions, makeOnnxLikeFloat32Tensor, OnnxExecutionProvider, OnnxLikeFloat32Tensor } from '../utilities/OnnxUtilities.js'
|
|
25
|
+
import { dmlProviderAvailable, getOnnxSessionOptions, makeOnnxLikeFloat32Tensor, OnnxExecutionProvider, OnnxLikeFloat32Tensor } from '../utilities/OnnxUtilities.js'
|
|
26
26
|
import { murmurHash3_int32Input } from '../utilities/Hashing.js'
|
|
27
|
+
import { containsInvalidCodepoint, getTokenRepetitionScore } from '../utilities/StringUtilities.js'
|
|
27
28
|
|
|
28
29
|
export async function recognize(
|
|
29
30
|
sourceRawAudio: RawAudio,
|
|
@@ -458,7 +459,7 @@ export class Whisper {
|
|
|
458
459
|
// Find alignment path
|
|
459
460
|
let alignmentHeads: number[] | undefined = undefined
|
|
460
461
|
|
|
461
|
-
if (
|
|
462
|
+
if (options.useOptimizedAlignmentHeads) {
|
|
462
463
|
alignmentHeads = this.alignmentHeadIndexes
|
|
463
464
|
}
|
|
464
465
|
|
|
@@ -565,12 +566,15 @@ export class Whisper {
|
|
|
565
566
|
topCandidateCount: 1,
|
|
566
567
|
punctuationThreshold: Infinity,
|
|
567
568
|
autoPromptParts: false,
|
|
568
|
-
maxTokensPerPart:
|
|
569
|
+
maxTokensPerPart: whisperAlignmentOptions.maxTokensPerPart!,
|
|
569
570
|
suppressRepetition: false,
|
|
570
571
|
repetitionThreshold: Infinity,
|
|
571
572
|
decodeTimestampTokens: true,
|
|
572
|
-
endTokenThreshold: whisperAlignmentOptions
|
|
573
|
+
endTokenThreshold: whisperAlignmentOptions.endTokenThreshold!,
|
|
573
574
|
includeEndTokenInCandidates: false,
|
|
575
|
+
useOptimizedAlignmentHeads: whisperAlignmentOptions.useOptimizedAlignmentHeads!,
|
|
576
|
+
encoderProvider: whisperAlignmentOptions.encoderProvider!,
|
|
577
|
+
decoderProvider: whisperAlignmentOptions.decoderProvider!,
|
|
574
578
|
seed: undefined,
|
|
575
579
|
}
|
|
576
580
|
|
|
@@ -1102,7 +1106,8 @@ export class Whisper {
|
|
|
1102
1106
|
const sampleRate = rawAudio.sampleRate
|
|
1103
1107
|
|
|
1104
1108
|
const fftOrder = 400
|
|
1105
|
-
const
|
|
1109
|
+
const fftWindowSize = 400
|
|
1110
|
+
const fftHopLength = 160
|
|
1106
1111
|
|
|
1107
1112
|
const filterbankCount = this.filterbankCount
|
|
1108
1113
|
const filterbanks = this.filterbanks
|
|
@@ -1127,7 +1132,7 @@ export class Whisper {
|
|
|
1127
1132
|
|
|
1128
1133
|
const rawAudioPart: RawAudio = { audioChannels: [paddedAudioSamples], sampleRate }
|
|
1129
1134
|
|
|
1130
|
-
const { melSpectogram } = await computeMelSpectogramUsingFilterbanks(rawAudioPart, fftOrder,
|
|
1135
|
+
const { melSpectogram } = await computeMelSpectogramUsingFilterbanks(rawAudioPart, fftOrder, fftWindowSize, fftHopLength, filterbanks)
|
|
1131
1136
|
|
|
1132
1137
|
await logger.startAsync('Normalize mel spectogram')
|
|
1133
1138
|
|
|
@@ -1841,17 +1846,17 @@ export function isEnglishOnlyModel(modelName: WhisperModelName) {
|
|
|
1841
1846
|
return modelName.endsWith('.en')
|
|
1842
1847
|
}
|
|
1843
1848
|
|
|
1844
|
-
function getDefaultEncoderProvidersForModel(modelName: WhisperModelName): OnnxExecutionProvider[] {
|
|
1845
|
-
if (
|
|
1849
|
+
export function getDefaultEncoderProvidersForModel(modelName: WhisperModelName): OnnxExecutionProvider[] {
|
|
1850
|
+
if (dmlProviderAvailable()) {
|
|
1846
1851
|
return ['dml', 'cpu']
|
|
1847
1852
|
} else {
|
|
1848
1853
|
return []
|
|
1849
1854
|
}
|
|
1850
1855
|
}
|
|
1851
1856
|
|
|
1852
|
-
function getDefaultDecoderProvidersForModel(modelName: WhisperModelName): OnnxExecutionProvider[] {
|
|
1857
|
+
export function getDefaultDecoderProvidersForModel(modelName: WhisperModelName): OnnxExecutionProvider[] {
|
|
1853
1858
|
if (modelName.startsWith('small') || modelName.startsWith('medium') || modelName.startsWith('large')) {
|
|
1854
|
-
if (
|
|
1859
|
+
if (dmlProviderAvailable()) {
|
|
1855
1860
|
return ['dml', 'cpu']
|
|
1856
1861
|
} else {
|
|
1857
1862
|
return []
|
|
@@ -2439,6 +2444,7 @@ export interface WhisperOptions {
|
|
|
2439
2444
|
decodeTimestampTokens?: boolean
|
|
2440
2445
|
endTokenThreshold?: number
|
|
2441
2446
|
includeEndTokenInCandidates?: boolean
|
|
2447
|
+
useOptimizedAlignmentHeads?: boolean,
|
|
2442
2448
|
encoderProvider?: OnnxExecutionProvider
|
|
2443
2449
|
decoderProvider?: OnnxExecutionProvider
|
|
2444
2450
|
seed?: number
|
|
@@ -2457,6 +2463,7 @@ export const defaultWhisperOptions: WhisperOptions = {
|
|
|
2457
2463
|
decodeTimestampTokens: true,
|
|
2458
2464
|
endTokenThreshold: 0.9,
|
|
2459
2465
|
includeEndTokenInCandidates: true,
|
|
2466
|
+
useOptimizedAlignmentHeads: true,
|
|
2460
2467
|
encoderProvider: undefined,
|
|
2461
2468
|
decoderProvider: undefined,
|
|
2462
2469
|
seed: undefined,
|
|
@@ -2466,6 +2473,9 @@ export const defaultWhisperOptions: WhisperOptions = {
|
|
|
2466
2473
|
export interface WhisperAlignmentOptions {
|
|
2467
2474
|
model?: WhisperModelName
|
|
2468
2475
|
endTokenThreshold?: number
|
|
2476
|
+
maxTokensPerPart?: number
|
|
2477
|
+
useOptimizedAlignmentHeads?: boolean
|
|
2478
|
+
|
|
2469
2479
|
encoderProvider?: OnnxExecutionProvider
|
|
2470
2480
|
decoderProvider?: OnnxExecutionProvider
|
|
2471
2481
|
}
|
|
@@ -2473,8 +2483,11 @@ export interface WhisperAlignmentOptions {
|
|
|
2473
2483
|
export const defaultWhisperAlignmentOptions: WhisperAlignmentOptions = {
|
|
2474
2484
|
model: undefined,
|
|
2475
2485
|
endTokenThreshold: 0.9,
|
|
2486
|
+
maxTokensPerPart: 250,
|
|
2487
|
+
useOptimizedAlignmentHeads: true,
|
|
2488
|
+
|
|
2476
2489
|
encoderProvider: undefined,
|
|
2477
|
-
decoderProvider: undefined
|
|
2490
|
+
decoderProvider: undefined,
|
|
2478
2491
|
}
|
|
2479
2492
|
|
|
2480
2493
|
// Language detection options
|