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.
Files changed (86) hide show
  1. package/data/schemas/options.json +30 -4
  2. package/dist/api/SourceSeparation.d.ts +3 -2
  3. package/dist/api/SourceSeparation.js +10 -8
  4. package/dist/api/SourceSeparation.js.map +1 -1
  5. package/dist/api/Synthesis.js +3 -1
  6. package/dist/api/Synthesis.js.map +1 -1
  7. package/dist/api/TextTranslation.d.ts +3 -1
  8. package/dist/api/TextTranslation.js +12 -6
  9. package/dist/api/TextTranslation.js.map +1 -1
  10. package/dist/build-tools/MakeTarballsForInstalledPackages.d.ts +1 -0
  11. package/dist/build-tools/MakeTarballsForInstalledPackages.js +20 -0
  12. package/dist/build-tools/MakeTarballsForInstalledPackages.js.map +1 -0
  13. package/dist/cli/CLI.js +4 -3
  14. package/dist/cli/CLI.js.map +1 -1
  15. package/dist/dsp/FFT.d.ts +2 -3
  16. package/dist/dsp/FFT.js +25 -23
  17. package/dist/dsp/FFT.js.map +1 -1
  18. package/dist/nlp/Segmentation.js +2 -1
  19. package/dist/nlp/Segmentation.js.map +1 -1
  20. package/dist/nlp/TextNormalizer.d.ts +2 -0
  21. package/dist/nlp/TextNormalizer.js +51 -0
  22. package/dist/nlp/TextNormalizer.js.map +1 -1
  23. package/dist/recognition/WhisperSTT.d.ts +2 -0
  24. package/dist/recognition/WhisperSTT.js +7 -6
  25. package/dist/recognition/WhisperSTT.js.map +1 -1
  26. package/dist/source-separation/MDXNetSourceSeparation.d.ts +20 -2
  27. package/dist/source-separation/MDXNetSourceSeparation.js +134 -76
  28. package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
  29. package/dist/subtitles/Subtitles.js +2 -1
  30. package/dist/subtitles/Subtitles.js.map +1 -1
  31. package/dist/synthesis/EspeakTTS.js +5 -5
  32. package/dist/synthesis/EspeakTTS.js.map +1 -1
  33. package/dist/tests/Test.js +0 -1
  34. package/dist/tests/Test.js.map +1 -1
  35. package/dist/text-translation/GoogleTranslateTextTranslation.d.ts +9 -3
  36. package/dist/text-translation/GoogleTranslateTextTranslation.js +59 -29
  37. package/dist/text-translation/GoogleTranslateTextTranslation.js.map +1 -1
  38. package/dist/utilities/OnnxUtilities.d.ts +1 -0
  39. package/dist/utilities/OnnxUtilities.js +5 -5
  40. package/dist/utilities/OnnxUtilities.js.map +1 -1
  41. package/dist/utilities/PackageManager.js +3 -0
  42. package/dist/utilities/PackageManager.js.map +1 -1
  43. package/dist/utilities/StringUtilities.d.ts +28 -0
  44. package/dist/utilities/StringUtilities.js +150 -0
  45. package/dist/utilities/StringUtilities.js.map +1 -0
  46. package/dist/utilities/TarballMaker.d.ts +3 -4
  47. package/dist/utilities/TarballMaker.js +6 -19
  48. package/dist/utilities/TarballMaker.js.map +1 -1
  49. package/dist/utilities/Timeline.js +2 -1
  50. package/dist/utilities/Timeline.js.map +1 -1
  51. package/dist/utilities/Utilities.d.ts +1 -33
  52. package/dist/utilities/Utilities.js +3 -176
  53. package/dist/utilities/Utilities.js.map +1 -1
  54. package/dist/utilities/WebReader.js +1 -1
  55. package/dist/utilities/WebReader.js.map +1 -1
  56. package/dist/utilities/WikipediaReader.js +1 -1
  57. package/dist/utilities/WikipediaReader.js.map +1 -1
  58. package/dist/utilities/WindowedList.d.ts +10 -0
  59. package/dist/utilities/WindowedList.js +36 -0
  60. package/dist/utilities/WindowedList.js.map +1 -0
  61. package/docs/Engines.md +1 -1
  62. package/docs/Options.md +8 -2
  63. package/package.json +8 -9
  64. package/src/api/SourceSeparation.ts +15 -10
  65. package/src/api/Synthesis.ts +3 -1
  66. package/src/api/TextTranslation.ts +17 -9
  67. package/src/build-tools/MakeTarballsForInstalledPackages.ts +25 -0
  68. package/src/cli/CLI.ts +4 -3
  69. package/src/dsp/FFT.ts +30 -33
  70. package/src/nlp/Segmentation.ts +2 -1
  71. package/src/nlp/TextNormalizer.ts +60 -0
  72. package/src/recognition/WhisperSTT.ts +7 -6
  73. package/src/source-separation/MDXNetSourceSeparation.ts +183 -85
  74. package/src/subtitles/Subtitles.ts +2 -1
  75. package/src/synthesis/EspeakTTS.ts +5 -5
  76. package/src/tests/Test.ts +0 -4
  77. package/src/text-translation/GoogleTranslateTextTranslation.ts +91 -30
  78. package/src/utilities/OnnxUtilities.ts +7 -8
  79. package/src/utilities/PackageManager.ts +3 -0
  80. package/src/utilities/StringUtilities.ts +203 -0
  81. package/src/utilities/TarballMaker.ts +6 -23
  82. package/src/utilities/Timeline.ts +2 -1
  83. package/src/utilities/Utilities.ts +4 -237
  84. package/src/utilities/WebReader.ts +1 -1
  85. package/src/utilities/WikipediaReader.ts +1 -1
  86. 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.start('Prepare for source separation')
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[] = mdxNetOptions.executionProvider ? [mdxNetOptions.executionProvider] : []
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 audioStereo44100 = await ensureRawAudio(inputRawAudio, 44100, 2)
49
+ const modelProfile = getProfileForMDXNetModelName(mdxNetOptions.model!)
44
50
 
45
- isolatedRawAudio = await MDXNetSourceSeparation.isolate(audioStereo44100, modelPath, executionProviders)
51
+ isolatedRawAudio = await MDXNetSourceSeparation.isolate(inputRawAudio44100Stereo, modelPath, modelProfile, executionProviders)
46
52
 
47
53
  logger.end()
48
54
 
49
- logger.start(`Subtract from original waveform to extract background audio`)
50
- backgroundRawAudio = subtractAudio(audioStereo44100, isolatedRawAudio)
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?: string
78
- executionProvider?: OnnxExecutionProvider
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
- executionProvider: undefined,
92
+ provider: undefined,
88
93
  }
89
94
  }
90
95
 
@@ -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, convertHtmlToText, sha256AsHex, simplifyPunctuationCharacters, stringifyAndFormatJson, logToStderr, yieldToEventLoop, runOperationWithRetries } from '../utilities/Utilities.js'
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 an early prototype implementation and doesn't work correctly.`, chalk.yellow, 'warning')
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 an early prototype implementation and doesn't work correctly.`, chalk.yellow, 'warning')
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
- nllb: {
155
+ plainText: {
156
+ paragraphBreaks: 'double',
157
+ whitespace: 'preserve'
152
158
  },
153
159
 
154
- googleTranslate: {
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 (early prototype implementation).',
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 (early prototype implementation).',
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 { convertHtmlToText, formatIntegerWithLeadingZeros, formatListWithQuotedElements, getWithDefault, logToStderr, setupUnhandledExceptionListeners, splitFilenameOnExtendedExtension, stringifyAndFormatJson } from '../utilities/Utilities.js'
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(`align only supports reference files with extensions 'txt', 'html', 'htm', 'srt' or 'vtt'`)
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(inputFileContent, options)
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 { RawAudio } from '../audio/AudioUtilities.js'
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
- frames.push(bins)
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
- export async function getKissFFTInstance() {
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
- export async function testFFT1(rawAudio: RawAudio) {
246
- const { resampleAudioSpeex } = await import('./SpeexResampler.js')
247
-
248
- const samples = (await resampleAudioSpeex(rawAudio, 16000)).audioChannels[0]
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
- const recoveredRawAudio: RawAudio = { audioChannels: [recoveredSamples], sampleRate: 16000 }
255
+ kissFFTInstance = await initializer()
256
+ }
260
257
 
261
- return recoveredRawAudio
258
+ return kissFFTInstance
262
259
  }
263
260
 
264
261
  export type WindowType = 'hann' | 'hamming' | 'povey'
@@ -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, includesAnyOf, indexOfAnyOf, logToStderr } from '../utilities/Utilities.js'
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, containsInvalidCodepoint, getIntegerRange, getTokenRepetitionScore, splitFloat32Array, yieldToEventLoop } from '../utilities/Utilities.js'
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 (process.platform === 'win32') {
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 (process.platform === 'win32') {
1855
+ if (dmlProviderAvailable()) {
1855
1856
  return ['dml', 'cpu']
1856
1857
  } else {
1857
1858
  return []