echogarden 1.6.2 → 1.7.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 +30 -4
- package/dist/api/SourceSeparation.d.ts +3 -2
- package/dist/api/SourceSeparation.js +10 -8
- package/dist/api/SourceSeparation.js.map +1 -1
- package/dist/api/Synthesis.js +3 -1
- 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/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 +2 -3
- package/dist/dsp/FFT.js +25 -23
- package/dist/dsp/FFT.js.map +1 -1
- 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 +2 -0
- package/dist/recognition/WhisperSTT.js +7 -6
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/source-separation/MDXNetSourceSeparation.d.ts +20 -2
- package/dist/source-separation/MDXNetSourceSeparation.js +134 -76
- 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 +3 -0
- 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/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 +8 -2
- package/package.json +8 -9
- package/src/api/SourceSeparation.ts +15 -10
- package/src/api/Synthesis.ts +3 -1
- package/src/api/TextTranslation.ts +17 -9
- package/src/build-tools/MakeTarballsForInstalledPackages.ts +25 -0
- package/src/cli/CLI.ts +4 -3
- package/src/dsp/FFT.ts +30 -33
- package/src/nlp/Segmentation.ts +2 -1
- package/src/nlp/TextNormalizer.ts +60 -0
- package/src/recognition/WhisperSTT.ts +7 -6
- package/src/source-separation/MDXNetSourceSeparation.ts +183 -85
- 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/utilities/OnnxUtilities.ts +7 -8
- package/src/utilities/PackageManager.ts +3 -0
- 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/WebReader.ts +1 -1
- package/src/utilities/WikipediaReader.ts +1 -1
- package/src/utilities/WindowedList.ts +45 -0
|
@@ -7,12 +7,13 @@ import chalk from 'chalk';
|
|
|
7
7
|
import { readdir } from '../utilities/FileSystem.js';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { OnnxExecutionProvider } from '../utilities/OnnxUtilities.js';
|
|
10
|
+
import { getProfileForMDXNetModelName, MDXNetModelName } from '../source-separation/MDXNetSourceSeparation.js';
|
|
10
11
|
|
|
11
12
|
export async function isolate(input: AudioSourceParam, options: SourceSeparationOptions): Promise<SourceSeparationResult> {
|
|
12
13
|
const logger = new Logger()
|
|
13
14
|
const startTimestamp = logger.getTimestamp()
|
|
14
15
|
|
|
15
|
-
logger.
|
|
16
|
+
await logger.startAsync('Prepare for source separation')
|
|
16
17
|
|
|
17
18
|
const inputRawAudio = await ensureRawAudio(input)
|
|
18
19
|
|
|
@@ -27,7 +28,8 @@ export async function isolate(input: AudioSourceParam, options: SourceSeparation
|
|
|
27
28
|
|
|
28
29
|
const mdxNetOptions = options.mdxNet!
|
|
29
30
|
|
|
30
|
-
const executionProviders: OnnxExecutionProvider[] =
|
|
31
|
+
const executionProviders: OnnxExecutionProvider[] =
|
|
32
|
+
mdxNetOptions.provider ? [mdxNetOptions.provider] : MDXNetSourceSeparation.getDefaultMDXNetProviders()
|
|
31
33
|
|
|
32
34
|
const packageDir = await loadPackage(`mdxnet-${mdxNetOptions.model!}`)
|
|
33
35
|
const modelFilename = (await readdir(packageDir)).filter(name => name.endsWith('onnx'))[0]
|
|
@@ -38,16 +40,20 @@ export async function isolate(input: AudioSourceParam, options: SourceSeparation
|
|
|
38
40
|
|
|
39
41
|
const modelPath = path.join(packageDir, modelFilename)
|
|
40
42
|
|
|
43
|
+
await logger.startAsync(`Convert audio to 44.1 kHz stereo`)
|
|
44
|
+
|
|
45
|
+
const inputRawAudio44100Stereo = await ensureRawAudio(inputRawAudio, 44100, 2)
|
|
46
|
+
|
|
41
47
|
logger.end()
|
|
42
48
|
|
|
43
|
-
const
|
|
49
|
+
const modelProfile = getProfileForMDXNetModelName(mdxNetOptions.model!)
|
|
44
50
|
|
|
45
|
-
isolatedRawAudio = await MDXNetSourceSeparation.isolate(
|
|
51
|
+
isolatedRawAudio = await MDXNetSourceSeparation.isolate(inputRawAudio44100Stereo, modelPath, modelProfile, executionProviders)
|
|
46
52
|
|
|
47
53
|
logger.end()
|
|
48
54
|
|
|
49
|
-
logger.
|
|
50
|
-
backgroundRawAudio = subtractAudio(
|
|
55
|
+
await logger.startAsync(`Subtract from original waveform to extract background audio`)
|
|
56
|
+
backgroundRawAudio = subtractAudio(inputRawAudio44100Stereo, isolatedRawAudio)
|
|
51
57
|
|
|
52
58
|
break
|
|
53
59
|
}
|
|
@@ -58,7 +64,6 @@ export async function isolate(input: AudioSourceParam, options: SourceSeparation
|
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
logger.end()
|
|
61
|
-
logger.log('')
|
|
62
67
|
logger.logDuration(`Total source separation time`, startTimestamp, chalk.magentaBright)
|
|
63
68
|
|
|
64
69
|
return {
|
|
@@ -74,8 +79,8 @@ export interface SourceSeparationOptions {
|
|
|
74
79
|
engine?: SourceSeparationEngine
|
|
75
80
|
|
|
76
81
|
mdxNet?: {
|
|
77
|
-
model?:
|
|
78
|
-
|
|
82
|
+
model?: MDXNetModelName
|
|
83
|
+
provider?: OnnxExecutionProvider
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
|
|
@@ -84,7 +89,7 @@ export const defaultSourceSeparationOptions: SourceSeparationOptions = {
|
|
|
84
89
|
|
|
85
90
|
mdxNet: {
|
|
86
91
|
model: 'UVR_MDXNET_1_9703',
|
|
87
|
-
|
|
92
|
+
provider: undefined,
|
|
88
93
|
}
|
|
89
94
|
}
|
|
90
95
|
|
package/src/api/Synthesis.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { deepClone, extendDeep } from '../utilities/ObjectUtilities.js'
|
|
|
4
4
|
|
|
5
5
|
import * as FFMpegTranscoder from '../codecs/FFMpegTranscoder.js'
|
|
6
6
|
|
|
7
|
-
import { clip,
|
|
7
|
+
import { clip, sha256AsHex, stringifyAndFormatJson, logToStderr, yieldToEventLoop, runOperationWithRetries } from '../utilities/Utilities.js'
|
|
8
8
|
import { RawAudio, attenuateIfClipping, concatAudioSegments, downmixToMono, encodeRawAudioToWave, getSamplePeakDecibels, getEmptyRawAudio, getRawAudioDuration, normalizeAudioLevel, trimAudioEnd, trimAudioStart } from '../audio/AudioUtilities.js'
|
|
9
9
|
import { Logger } from '../utilities/Logger.js'
|
|
10
10
|
|
|
@@ -25,6 +25,8 @@ import { type EspeakOptions } from '../synthesis/EspeakTTS.js'
|
|
|
25
25
|
import { type OpenAICloudTTSOptions } from '../synthesis/OpenAICloudTTS.js'
|
|
26
26
|
import { type ElevenlabsTTSOptions } from '../synthesis/ElevenlabsTTS.js'
|
|
27
27
|
import { OnnxExecutionProvider } from '../utilities/OnnxUtilities.js'
|
|
28
|
+
import { simplifyPunctuationCharacters } from '../nlp/TextNormalizer.js'
|
|
29
|
+
import { convertHtmlToText } from '../utilities/StringUtilities.js'
|
|
28
30
|
|
|
29
31
|
const log = logToStderr
|
|
30
32
|
|
|
@@ -3,6 +3,7 @@ import { formatLanguageCodeWithName, normalizeIdentifierToLanguageCode, parseLan
|
|
|
3
3
|
import { Logger } from '../utilities/Logger.js'
|
|
4
4
|
import { extendDeep } from '../utilities/ObjectUtilities.js'
|
|
5
5
|
import * as API from './API.js'
|
|
6
|
+
import { defaultGoogleTranslateTextTranslationOptions, type GoogleTranslateTextTranslationOptions } from '../text-translation/GoogleTranslateTextTranslation.js'
|
|
6
7
|
|
|
7
8
|
export async function translateText(inputText: string, options: TextTranslationOptions): Promise<TextTranslationResult> {
|
|
8
9
|
const logger = new Logger()
|
|
@@ -43,7 +44,7 @@ export async function translateText(inputText: string, options: TextTranslationO
|
|
|
43
44
|
|
|
44
45
|
logger.end()
|
|
45
46
|
|
|
46
|
-
logger.logTitledMessage(`Warning`, `The nllb engine is currently
|
|
47
|
+
logger.logTitledMessage(`Warning`, `The nllb text translation engine is currently a work-in-progress and doesn't work correctly.`, chalk.yellow, 'warning')
|
|
47
48
|
|
|
48
49
|
translationPairs = await NLLBTextTranslation.translateText(inputText, options.sourceLanguage, options.targetLanguage)
|
|
49
50
|
|
|
@@ -63,9 +64,11 @@ export async function translateText(inputText: string, options: TextTranslationO
|
|
|
63
64
|
case 'google-translate': {
|
|
64
65
|
const GoogleTranslateTextTranslation = await import('../text-translation/GoogleTranslateTextTranslation.js')
|
|
65
66
|
|
|
67
|
+
const googleTranslateOptions = options.googleTranslate!
|
|
68
|
+
|
|
66
69
|
logger.end();
|
|
67
70
|
|
|
68
|
-
({ translationPairs, translatedText } = await GoogleTranslateTextTranslation.translateText(inputText, options.sourceLanguage, options.targetLanguage))
|
|
71
|
+
({ translationPairs, translatedText } = await GoogleTranslateTextTranslation.translateText(inputText, options.sourceLanguage, options.targetLanguage, options.plainText!, googleTranslateOptions))
|
|
69
72
|
|
|
70
73
|
break
|
|
71
74
|
}
|
|
@@ -75,7 +78,7 @@ export async function translateText(inputText: string, options: TextTranslationO
|
|
|
75
78
|
|
|
76
79
|
logger.end()
|
|
77
80
|
|
|
78
|
-
logger.logTitledMessage(`Warning`, `The deepl engine is currently
|
|
81
|
+
logger.logTitledMessage(`Warning`, `The deepl text translation engine is currently a work-in-progress and doesn't work correctly.`, chalk.yellow, 'warning')
|
|
79
82
|
|
|
80
83
|
translationPairs = await DeepLTextTranslation.translateText(inputText, options.sourceLanguage, options.targetLanguage)
|
|
81
84
|
translatedText = ''
|
|
@@ -113,11 +116,12 @@ export interface TextTranslationOptions {
|
|
|
113
116
|
|
|
114
117
|
languageDetection?: API.TextLanguageDetectionOptions
|
|
115
118
|
|
|
119
|
+
plainText?: API.PlainTextOptions
|
|
120
|
+
|
|
116
121
|
nllb?: {
|
|
117
122
|
},
|
|
118
123
|
|
|
119
|
-
googleTranslate?:
|
|
120
|
-
},
|
|
124
|
+
googleTranslate?: GoogleTranslateTextTranslationOptions,
|
|
121
125
|
|
|
122
126
|
deepl?: {
|
|
123
127
|
},
|
|
@@ -148,12 +152,16 @@ export const defaultTextTranslationOptions: TextTranslationOptions = {
|
|
|
148
152
|
|
|
149
153
|
languageDetection: undefined,
|
|
150
154
|
|
|
151
|
-
|
|
155
|
+
plainText: {
|
|
156
|
+
paragraphBreaks: 'double',
|
|
157
|
+
whitespace: 'preserve'
|
|
152
158
|
},
|
|
153
159
|
|
|
154
|
-
|
|
160
|
+
nllb: {
|
|
155
161
|
},
|
|
156
162
|
|
|
163
|
+
googleTranslate: defaultGoogleTranslateTextTranslationOptions,
|
|
164
|
+
|
|
157
165
|
deepl: {
|
|
158
166
|
},
|
|
159
167
|
}
|
|
@@ -162,7 +170,7 @@ export const textTranslationEngines: API.EngineMetadata[] = [
|
|
|
162
170
|
{
|
|
163
171
|
id: 'nllb',
|
|
164
172
|
name: 'NLLB',
|
|
165
|
-
description: 'No Language Left Behind (NLLB) is a deep learning machine translation model by Facebook Research (
|
|
173
|
+
description: 'No Language Left Behind (NLLB) is a deep learning machine translation model by Facebook Research (work-in-progress, do not use).',
|
|
166
174
|
type: 'local'
|
|
167
175
|
},
|
|
168
176
|
{
|
|
@@ -174,7 +182,7 @@ export const textTranslationEngines: API.EngineMetadata[] = [
|
|
|
174
182
|
{
|
|
175
183
|
id: 'deepl',
|
|
176
184
|
name: 'DeepL',
|
|
177
|
-
description: 'Unoffical text translation API used by the DeepL web interface (
|
|
185
|
+
description: 'Unoffical text translation API used by the DeepL web interface (work-in-progress, do not use).',
|
|
178
186
|
type: 'cloud'
|
|
179
187
|
},
|
|
180
188
|
]
|
|
@@ -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,24 @@
|
|
|
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
5
|
let kissFFTInstance: any
|
|
7
6
|
|
|
7
|
+
// Compute short-term Fourier transform (real-valued)
|
|
8
8
|
export async function stftr(samples: Float32Array, fftOrder: number, windowSize: number, hopSize: number, windowType: WindowType) {
|
|
9
|
+
const frames: Float32Array[] = []
|
|
10
|
+
|
|
11
|
+
for await (const frame of stftrGenerator(samples, fftOrder, windowSize, hopSize, windowType)) {
|
|
12
|
+
frames.push(frame)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return frames
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Incrementally generate short-term Fourier transform frames (real-valued)
|
|
19
|
+
export async function* stftrGenerator(samples: Float32Array, fftOrder: number, windowSize: number, hopSize: number, windowType: WindowType) {
|
|
9
20
|
if (fftOrder % 2 != 0 || windowSize % 2 != 0) {
|
|
10
|
-
throw new Error('FFT order and window size must multiples of 2')
|
|
21
|
+
throw new Error('FFT order and window size must be multiples of 2')
|
|
11
22
|
}
|
|
12
23
|
|
|
13
24
|
if (windowSize > fftOrder) {
|
|
@@ -35,8 +46,6 @@ export async function stftr(samples: Float32Array, fftOrder: number, windowSize:
|
|
|
35
46
|
const frameBufferRef = wasmMemory.allocFloat32Array(fftOrder)
|
|
36
47
|
const binsBufferRef = wasmMemory.allocFloat32Array(fftOrder * 2)
|
|
37
48
|
|
|
38
|
-
const frames: Float32Array[] = []
|
|
39
|
-
|
|
40
49
|
for (let offset = 0; offset < sampleCount; offset += hopSize) {
|
|
41
50
|
const windowSamples = samples.subarray(offset, offset + windowSize)
|
|
42
51
|
frameBufferRef.clear()
|
|
@@ -52,14 +61,14 @@ export async function stftr(samples: Float32Array, fftOrder: number, windowSize:
|
|
|
52
61
|
m._kiss_fftr(statePtr, frameBufferRef.address, binsBufferRef.address)
|
|
53
62
|
|
|
54
63
|
const bins = binsBufferRef.view.slice(0, fftOrder + 2)
|
|
55
|
-
|
|
64
|
+
|
|
65
|
+
yield bins
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
wasmMemory.freeAll()
|
|
59
|
-
|
|
60
|
-
return frames
|
|
61
69
|
}
|
|
62
70
|
|
|
71
|
+
// Compute short-term inverse Fourier transform (real-valued)
|
|
63
72
|
export async function stiftr(binsForFrames: Float32Array[], fftOrder: number, windowSize: number, hopSize: number, windowType: WindowType, expectedOutputLength?: number) {
|
|
64
73
|
if (fftOrder % 2 != 0 || windowSize % 2 != 0) {
|
|
65
74
|
throw new Error('FFT order and window size must multiples of 2')
|
|
@@ -135,6 +144,7 @@ export async function stiftr(binsForFrames: Float32Array[], fftOrder: number, wi
|
|
|
135
144
|
return outSamplesTrimmed
|
|
136
145
|
}
|
|
137
146
|
|
|
147
|
+
// Get bin frequency thresholds for a particular bin count and maximum frequency
|
|
138
148
|
export function getBinFrequencies(binCount: number, maxFrequency: number) {
|
|
139
149
|
const binFrequencies = new Float32Array(binCount)
|
|
140
150
|
const frequencyStep = maxFrequency / (binCount - 1)
|
|
@@ -146,10 +156,12 @@ export function getBinFrequencies(binCount: number, maxFrequency: number) {
|
|
|
146
156
|
return binFrequencies
|
|
147
157
|
}
|
|
148
158
|
|
|
159
|
+
// Convert an array of raw FFT frames to a power spectrum
|
|
149
160
|
export function fftFramesToPowerSpectogram(fftFrames: Float32Array[]) {
|
|
150
161
|
return fftFrames.map(fftFrame => fftFrameToPowerSpectrum(fftFrame))
|
|
151
162
|
}
|
|
152
163
|
|
|
164
|
+
// Convert a raw FFT frame to a power spectrum
|
|
153
165
|
export function fftFrameToPowerSpectrum(fftFrame: Float32Array) {
|
|
154
166
|
const powerSpectrum = new Float32Array(fftFrame.length / 2)
|
|
155
167
|
|
|
@@ -165,6 +177,7 @@ export function fftFrameToPowerSpectrum(fftFrame: Float32Array) {
|
|
|
165
177
|
return powerSpectrum
|
|
166
178
|
}
|
|
167
179
|
|
|
180
|
+
// Convert raw FFT frames to an array of complex numbers
|
|
168
181
|
export function binBufferToComplex(bins: Float32Array, extendAndMirror = false) {
|
|
169
182
|
const complexBins: ComplexNumber[] = []
|
|
170
183
|
|
|
@@ -191,6 +204,7 @@ export function binBufferToComplex(bins: Float32Array, extendAndMirror = false)
|
|
|
191
204
|
return complexBins
|
|
192
205
|
}
|
|
193
206
|
|
|
207
|
+
// Convert an array of complex numbers to raw FFT frames
|
|
194
208
|
export function complexToBinBuffer(complexBins: ComplexNumber[]) {
|
|
195
209
|
const binBuffer = new Float32Array(complexBins.length * 2)
|
|
196
210
|
|
|
@@ -204,16 +218,7 @@ export function complexToBinBuffer(complexBins: ComplexNumber[]) {
|
|
|
204
218
|
return binBuffer
|
|
205
219
|
}
|
|
206
220
|
|
|
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
|
-
|
|
221
|
+
// Get window weights for a particular window function
|
|
217
222
|
export function getWindowWeights(windowType: WindowType, windowSize: number) {
|
|
218
223
|
const weights = new Float32Array(windowSize)
|
|
219
224
|
|
|
@@ -242,23 +247,15 @@ export function getWindowWeights(windowType: WindowType, windowSize: number) {
|
|
|
242
247
|
return weights
|
|
243
248
|
}
|
|
244
249
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const fftOrder = 512
|
|
251
|
-
const windowSize = 320
|
|
252
|
-
const hopLength = windowSize / 2
|
|
253
|
-
const windowType: WindowType = 'hann'
|
|
254
|
-
|
|
255
|
-
const bins = await stftr(samples, fftOrder, windowSize, hopLength, windowType)
|
|
256
|
-
const normalizedBins = bins.map(bin => bin.map(x => x / fftOrder))
|
|
257
|
-
const recoveredSamples = await stiftr(normalizedBins, fftOrder, windowSize, hopLength, windowType, samples.length)
|
|
250
|
+
// Get KISS FFT instance (initialize new if not exists)
|
|
251
|
+
export async function getKissFFTInstance() {
|
|
252
|
+
if (!kissFFTInstance) {
|
|
253
|
+
const { default: initializer } = await import('@echogarden/kissfft-wasm')
|
|
258
254
|
|
|
259
|
-
|
|
255
|
+
kissFFTInstance = await initializer()
|
|
256
|
+
}
|
|
260
257
|
|
|
261
|
-
return
|
|
258
|
+
return kissFFTInstance
|
|
262
259
|
}
|
|
263
260
|
|
|
264
261
|
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,
|
|
@@ -1841,17 +1842,17 @@ export function isEnglishOnlyModel(modelName: WhisperModelName) {
|
|
|
1841
1842
|
return modelName.endsWith('.en')
|
|
1842
1843
|
}
|
|
1843
1844
|
|
|
1844
|
-
function getDefaultEncoderProvidersForModel(modelName: WhisperModelName): OnnxExecutionProvider[] {
|
|
1845
|
-
if (
|
|
1845
|
+
export function getDefaultEncoderProvidersForModel(modelName: WhisperModelName): OnnxExecutionProvider[] {
|
|
1846
|
+
if (dmlProviderAvailable()) {
|
|
1846
1847
|
return ['dml', 'cpu']
|
|
1847
1848
|
} else {
|
|
1848
1849
|
return []
|
|
1849
1850
|
}
|
|
1850
1851
|
}
|
|
1851
1852
|
|
|
1852
|
-
function getDefaultDecoderProvidersForModel(modelName: WhisperModelName): OnnxExecutionProvider[] {
|
|
1853
|
+
export function getDefaultDecoderProvidersForModel(modelName: WhisperModelName): OnnxExecutionProvider[] {
|
|
1853
1854
|
if (modelName.startsWith('small') || modelName.startsWith('medium') || modelName.startsWith('large')) {
|
|
1854
|
-
if (
|
|
1855
|
+
if (dmlProviderAvailable()) {
|
|
1855
1856
|
return ['dml', 'cpu']
|
|
1856
1857
|
} else {
|
|
1857
1858
|
return []
|