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.
Files changed (115) hide show
  1. package/data/schemas/options.json +50 -12
  2. package/dist/api/Alignment.js +2 -2
  3. package/dist/api/Alignment.js.map +1 -1
  4. package/dist/api/Denoising.js +5 -4
  5. package/dist/api/Denoising.js.map +1 -1
  6. package/dist/api/Recognition.js +2 -2
  7. package/dist/api/Recognition.js.map +1 -1
  8. package/dist/api/SourceSeparation.d.ts +2 -5
  9. package/dist/api/SourceSeparation.js +14 -13
  10. package/dist/api/SourceSeparation.js.map +1 -1
  11. package/dist/api/SpeechLanguageDetection.js +2 -2
  12. package/dist/api/SpeechLanguageDetection.js.map +1 -1
  13. package/dist/api/SpeechTranslation.js +2 -2
  14. package/dist/api/SpeechTranslation.js.map +1 -1
  15. package/dist/api/Synthesis.js +8 -6
  16. package/dist/api/Synthesis.js.map +1 -1
  17. package/dist/api/TextTranslation.d.ts +3 -1
  18. package/dist/api/TextTranslation.js +12 -6
  19. package/dist/api/TextTranslation.js.map +1 -1
  20. package/dist/api/TranslationAlignment.js +2 -2
  21. package/dist/api/TranslationAlignment.js.map +1 -1
  22. package/dist/audio/AudioUtilities.d.ts +5 -3
  23. package/dist/audio/AudioUtilities.js +39 -25
  24. package/dist/audio/AudioUtilities.js.map +1 -1
  25. package/dist/build-tools/MakeTarballsForInstalledPackages.d.ts +1 -0
  26. package/dist/build-tools/MakeTarballsForInstalledPackages.js +20 -0
  27. package/dist/build-tools/MakeTarballsForInstalledPackages.js.map +1 -0
  28. package/dist/cli/CLI.js +4 -3
  29. package/dist/cli/CLI.js.map +1 -1
  30. package/dist/dsp/FFT.d.ts +3 -3
  31. package/dist/dsp/FFT.js +55 -34
  32. package/dist/dsp/FFT.js.map +1 -1
  33. package/dist/nlp/IPA.d.ts +3 -3
  34. package/dist/nlp/Segmentation.js +2 -1
  35. package/dist/nlp/Segmentation.js.map +1 -1
  36. package/dist/nlp/TextNormalizer.d.ts +2 -0
  37. package/dist/nlp/TextNormalizer.js +51 -0
  38. package/dist/nlp/TextNormalizer.js.map +1 -1
  39. package/dist/recognition/WhisperSTT.d.ts +5 -0
  40. package/dist/recognition/WhisperSTT.js +19 -11
  41. package/dist/recognition/WhisperSTT.js.map +1 -1
  42. package/dist/source-separation/MDXNetSourceSeparation.d.ts +26 -3
  43. package/dist/source-separation/MDXNetSourceSeparation.js +159 -79
  44. package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
  45. package/dist/subtitles/Subtitles.js +2 -1
  46. package/dist/subtitles/Subtitles.js.map +1 -1
  47. package/dist/synthesis/EspeakTTS.js +5 -5
  48. package/dist/synthesis/EspeakTTS.js.map +1 -1
  49. package/dist/tests/Test.js +0 -1
  50. package/dist/tests/Test.js.map +1 -1
  51. package/dist/text-translation/GoogleTranslateTextTranslation.d.ts +9 -3
  52. package/dist/text-translation/GoogleTranslateTextTranslation.js +59 -29
  53. package/dist/text-translation/GoogleTranslateTextTranslation.js.map +1 -1
  54. package/dist/utilities/OnnxUtilities.d.ts +1 -0
  55. package/dist/utilities/OnnxUtilities.js +5 -5
  56. package/dist/utilities/OnnxUtilities.js.map +1 -1
  57. package/dist/utilities/PackageManager.js +6 -6
  58. package/dist/utilities/PackageManager.js.map +1 -1
  59. package/dist/utilities/StringUtilities.d.ts +28 -0
  60. package/dist/utilities/StringUtilities.js +150 -0
  61. package/dist/utilities/StringUtilities.js.map +1 -0
  62. package/dist/utilities/TarballMaker.d.ts +3 -4
  63. package/dist/utilities/TarballMaker.js +6 -19
  64. package/dist/utilities/TarballMaker.js.map +1 -1
  65. package/dist/utilities/Timeline.js +2 -1
  66. package/dist/utilities/Timeline.js.map +1 -1
  67. package/dist/utilities/Utilities.d.ts +1 -33
  68. package/dist/utilities/Utilities.js +3 -176
  69. package/dist/utilities/Utilities.js.map +1 -1
  70. package/dist/utilities/WasmMemoryManager.d.ts +9 -1
  71. package/dist/utilities/WasmMemoryManager.js +24 -3
  72. package/dist/utilities/WasmMemoryManager.js.map +1 -1
  73. package/dist/utilities/WebReader.js +1 -1
  74. package/dist/utilities/WebReader.js.map +1 -1
  75. package/dist/utilities/WikipediaReader.js +1 -1
  76. package/dist/utilities/WikipediaReader.js.map +1 -1
  77. package/dist/utilities/WindowedList.d.ts +10 -0
  78. package/dist/utilities/WindowedList.js +36 -0
  79. package/dist/utilities/WindowedList.js.map +1 -0
  80. package/docs/Engines.md +1 -1
  81. package/docs/Options.md +16 -10
  82. package/package.json +11 -11
  83. package/src/alignment/SpeechAlignment.ts +1 -1
  84. package/src/api/Alignment.ts +2 -2
  85. package/src/api/Denoising.ts +5 -4
  86. package/src/api/Recognition.ts +3 -3
  87. package/src/api/SourceSeparation.ts +20 -19
  88. package/src/api/SpeechLanguageDetection.ts +2 -2
  89. package/src/api/SpeechTranslation.ts +2 -2
  90. package/src/api/Synthesis.ts +8 -6
  91. package/src/api/TextTranslation.ts +17 -9
  92. package/src/api/TranslationAlignment.ts +2 -2
  93. package/src/audio/AudioUtilities.ts +53 -33
  94. package/src/build-tools/MakeTarballsForInstalledPackages.ts +25 -0
  95. package/src/cli/CLI.ts +4 -3
  96. package/src/dsp/FFT.ts +65 -42
  97. package/src/nlp/Segmentation.ts +2 -1
  98. package/src/nlp/TextNormalizer.ts +60 -0
  99. package/src/recognition/WhisperSTT.ts +25 -12
  100. package/src/source-separation/MDXNetSourceSeparation.ts +218 -88
  101. package/src/subtitles/Subtitles.ts +2 -1
  102. package/src/synthesis/EspeakTTS.ts +5 -5
  103. package/src/tests/Test.ts +0 -4
  104. package/src/text-translation/GoogleTranslateTextTranslation.ts +91 -30
  105. package/src/typings/Fillers.d.ts +2 -0
  106. package/src/utilities/OnnxUtilities.ts +7 -8
  107. package/src/utilities/PackageManager.ts +6 -7
  108. package/src/utilities/StringUtilities.ts +203 -0
  109. package/src/utilities/TarballMaker.ts +6 -23
  110. package/src/utilities/Timeline.ts +2 -1
  111. package/src/utilities/Utilities.ts +4 -237
  112. package/src/utilities/WasmMemoryManager.ts +38 -4
  113. package/src/utilities/WebReader.ts +1 -1
  114. package/src/utilities/WikipediaReader.ts +1 -1
  115. package/src/utilities/WindowedList.ts +45 -0
@@ -1,107 +1,151 @@
1
1
  import type * as Onnx from 'onnxruntime-node'
2
- import { RawAudio } from '../audio/AudioUtilities.js';
3
- import { binBufferToComplex, complexToBinBuffer, getWindowWeights, stftr, stiftr } from '../dsp/FFT.js';
4
- import { ComplexNumber } from '../math/VectorMath.js';
5
- import { logToStderr } from '../utilities/Utilities.js';
6
- import { Logger } from '../utilities/Logger.js';
7
- import { OnnxExecutionProvider, getOnnxSessionOptions } from '../utilities/OnnxUtilities.js';
2
+ import { getEmptyRawAudio, RawAudio } from '../audio/AudioUtilities.js'
3
+ import { getWindowWeights, stftrGenerator, stiftr, WindowType } from '../dsp/FFT.js'
4
+ import { logToStderr } from '../utilities/Utilities.js'
5
+ import { Logger } from '../utilities/Logger.js'
6
+ import { OnnxExecutionProvider, dmlProviderAvailable, getOnnxSessionOptions } from '../utilities/OnnxUtilities.js'
7
+ import chalk from 'chalk'
8
+ import { WindowedList } from '../utilities/WindowedList.js'
9
+ import { logLevelGreaterOrEqualTo } from '../api/API.js'
8
10
 
9
11
  const log = logToStderr
10
12
 
11
13
  export async function isolate(
12
14
  rawAudio: RawAudio,
13
15
  modelFilePath: string,
14
- executionProviders: OnnxExecutionProvider[]) {
16
+ modelProfile: MDXNetModelProfile,
17
+ options: MDXNetOptions) {
15
18
 
16
- const model = new MDXNet(modelFilePath, executionProviders)
19
+ const model = new MDXNet(modelFilePath, modelProfile, options)
17
20
 
18
21
  return model.processAudio(rawAudio)
19
22
  }
20
23
 
21
24
  export class MDXNet {
22
25
  session?: Onnx.InferenceSession
26
+ onnxSessionOptions?: Onnx.InferenceSession.SessionOptions
23
27
 
24
28
  constructor(
25
29
  public readonly modelFilePath: string,
26
- public readonly executionProviders: OnnxExecutionProvider[]) {
30
+ public readonly modelProfile: MDXNetModelProfile,
31
+ public readonly options: MDXNetOptions) {
27
32
  }
28
33
 
29
34
  async processAudio(rawAudio: RawAudio) {
30
- if (rawAudio.audioChannels.length != 2) {
35
+ if (rawAudio.audioChannels.length !== 2) {
31
36
  throw new Error(`Input audio must be stereo`)
32
37
  }
33
38
 
34
- if (rawAudio.sampleRate != 44100) {
35
- throw new Error(`Input audio must have a sample rate of 44100 Hz`)
39
+ if (rawAudio.sampleRate !== this.modelProfile.sampleRate) {
40
+ throw new Error(`Input audio must have a sample rate of ${this.modelProfile.sampleRate} Hz`)
36
41
  }
37
42
 
38
- await this.initializeSessionIfNeeded()
43
+ if (rawAudio.audioChannels[0].length === 0) {
44
+ return getEmptyRawAudio(rawAudio.audioChannels.length, rawAudio.sampleRate)
45
+ }
39
46
 
40
- const Onnx = await import('onnxruntime-node')
47
+ const enableTraceLogging = logLevelGreaterOrEqualTo('trace')
41
48
 
42
49
  const logger = new Logger()
43
50
 
44
- const session = this.session!
51
+ await logger.startAsync(`Initialize session for MDX-NET model '${this.options.model!}'`)
52
+
53
+ await this.initializeSessionIfNeeded()
54
+
55
+ logger.end()
56
+
57
+ logger.logTitledMessage(`Using ONNX execution provider`, `${this.onnxSessionOptions!.executionProviders!.join(', ')}`)
58
+
59
+ const Onnx = await import('onnxruntime-node')
60
+
61
+ const sampleRate = this.modelProfile.sampleRate
62
+ const fftSize = this.modelProfile.fftSize
63
+ const fftWindowSize = this.modelProfile.fftWindowSize
64
+ const fftHopSize = this.modelProfile.fftHopSize
65
+ const fftWindowType = this.modelProfile.fftWindowType
45
66
 
46
- const sampleRate = rawAudio.sampleRate
47
- const fftSize = 6144
48
- const fftCount = 2048
49
- const fftWindowSize = fftSize
50
- const fftHopSize = 1024
67
+ const binCount = this.modelProfile.binCount
51
68
 
52
- const segmentSize = 256
53
- const segmentHopSize = 240
69
+ const segmentSize = this.modelProfile.segmentSize
70
+ const segmentHopSize = this.modelProfile.segmentHopSize
54
71
 
55
72
  const sampleCount = rawAudio.audioChannels[0].length
56
73
 
57
- logger.start('Compute STFT of full waveform')
74
+ const fftSizeReciprocal = 1 / fftSize
58
75
 
59
- const fftFramesLeft = await stftr(rawAudio.audioChannels[0], fftSize, fftWindowSize, fftHopSize, 'hann')
60
- const fftFramesRight = await stftr(rawAudio.audioChannels[1], fftSize, fftWindowSize, fftHopSize, 'hann')
76
+ // Initialize generators for STFT frames for each channel
77
+ const fftFramesLeftGenerator = stftrGenerator(rawAudio.audioChannels[0], fftSize, fftWindowSize, fftHopSize, fftWindowType)
78
+ const fftFramesRightGenerator = stftrGenerator(rawAudio.audioChannels[1], fftSize, fftWindowSize, fftHopSize, fftWindowType)
61
79
 
62
- const fftFramesLeftComplex = fftFramesLeft.map(frame => binBufferToComplex(frame).slice(0, fftCount))
63
- const fftFramesRightComplex = fftFramesRight.map(frame => binBufferToComplex(frame).slice(0, fftCount))
80
+ // Initial windowed lists to store recently computed STFT frames
81
+ const fftFramesLeftWindowedList = new WindowedList<Float32Array>(segmentSize)
82
+ const fftFramesRightWindowedList = new WindowedList<Float32Array>(segmentSize)
64
83
 
65
84
  const audioForSegments: Float32Array[][] = []
66
85
 
67
- for (let segmentOffset = 0; segmentOffset < fftFramesLeft.length; segmentOffset += segmentHopSize) {
68
- const timePosition = segmentOffset * (fftHopSize / sampleRate)
86
+ for (let segmentStartFrameOffset = 0; ; segmentStartFrameOffset += segmentHopSize) {
87
+ const segmentEndFrameOffset = segmentStartFrameOffset + segmentSize
69
88
 
70
- logger.start(`Process segment at time position ${timePosition.toFixed(2)}`)
89
+ const timePosition = segmentStartFrameOffset * (fftHopSize / sampleRate)
71
90
 
72
- const fftFramesLeftComplexForSegment = fftFramesLeftComplex.slice(segmentOffset, segmentOffset + segmentSize)
73
- const fftFramesRightComplexForSegment = fftFramesRightComplex.slice(segmentOffset, segmentOffset + segmentSize)
91
+ if (enableTraceLogging) {
92
+ await logger.startAsync(`Compute STFT of segment at time position ${timePosition.toFixed(2)}`, undefined, chalk.magentaBright)
93
+ } else {
94
+ await logger.startAsync(`Process segment at time position ${timePosition.toFixed(2)}`)
95
+ }
74
96
 
75
- const segmentLength = fftFramesLeftComplexForSegment.length
97
+ while (fftFramesLeftWindowedList.endOffset < segmentEndFrameOffset) {
98
+ const nextLeftFrameResult = await fftFramesLeftGenerator.next()
99
+
100
+ if (nextLeftFrameResult.done) {
101
+ break
102
+ }
76
103
 
77
- const flattenedInputTensor = new Float32Array(1 * 4 * fftCount * segmentSize)
104
+ const nextRightFrameResult = await fftFramesRightGenerator.next()
105
+
106
+ if (nextRightFrameResult.done) {
107
+ break
108
+ }
109
+
110
+ fftFramesLeftWindowedList.add(nextLeftFrameResult.value)
111
+ fftFramesRightWindowedList.add(nextRightFrameResult.value)
112
+ }
113
+
114
+ const fftFramesForSegment = [
115
+ fftFramesLeftWindowedList.slice(segmentStartFrameOffset, segmentEndFrameOffset),
116
+ fftFramesRightWindowedList.slice(segmentStartFrameOffset, segmentEndFrameOffset)
117
+ ]
118
+
119
+ const segmentLength = fftFramesForSegment[0].length
120
+
121
+ const isLastSegment = segmentLength < segmentSize
122
+
123
+ if (enableTraceLogging) {
124
+ await logger.startAsync(`Reshape STFT frames`)
125
+ }
126
+
127
+ const flattenedInputTensor = new Float32Array(1 * 4 * binCount * segmentSize)
78
128
 
79
129
  {
80
130
  let writePosition = 0
81
131
 
82
- for (let tensorChannelIndex = 0; tensorChannelIndex < 4; tensorChannelIndex++) {
83
- const isEvenTensorChannelIndex = tensorChannelIndex % 2 === 0
84
- const inChannelIndex = tensorChannelIndex < 2 ? 0 : 1
132
+ // 4 tensor elements are structured as:
133
+ // <Channel 0 real> <Channel 0 imaginary> <Channel 1 real> <Channel 1 imaginary>
134
+ for (let tensorElementIndex = 0; tensorElementIndex < 4; tensorElementIndex++) {
135
+ const isRealComponentTensorElementIndex = tensorElementIndex % 2 === 0
136
+ const audioChannelIndex = tensorElementIndex < 2 ? 0 : 1
85
137
 
86
- for (let binIndex = 0; binIndex < fftCount; binIndex++) {
138
+ for (let binIndex = 0; binIndex < binCount; binIndex++) {
87
139
  for (let frameIndex = 0; frameIndex < segmentSize; frameIndex++) {
88
140
  let value = 0
89
141
 
90
- if (frameIndex < segmentLength && binIndex >= 0) {
91
- let frame: ComplexNumber[]
142
+ if (frameIndex < segmentLength) {
143
+ const frame = fftFramesForSegment[audioChannelIndex][frameIndex]
92
144
 
93
- if (inChannelIndex === 0) {
94
- frame = fftFramesLeftComplexForSegment[frameIndex]
145
+ if (isRealComponentTensorElementIndex) {
146
+ value = frame[binIndex << 1]
95
147
  } else {
96
- frame = fftFramesRightComplexForSegment[frameIndex]
97
- }
98
-
99
- const bin = frame[binIndex]
100
-
101
- if (isEvenTensorChannelIndex) {
102
- value = bin.real
103
- } else {
104
- value = bin.imaginary
148
+ value = frame[(binIndex << 1) + 1]
105
149
  }
106
150
  }
107
151
 
@@ -111,51 +155,51 @@ export class MDXNet {
111
155
  }
112
156
  }
113
157
 
114
- const inputTensor = new Onnx.Tensor('float32', flattenedInputTensor, [1, 4, 2048, 256])
158
+ if (enableTraceLogging) {
159
+ await logger.startAsync(`Process segment with MDXNet model`)
160
+ }
115
161
 
116
- logger.start('Run MDXNet model')
162
+ const inputTensor = new Onnx.Tensor('float32', flattenedInputTensor, [1, 4, binCount, segmentSize])
117
163
 
118
- const { output: outputTensor } = await session.run({ input: inputTensor })
164
+ const { output: outputTensor } = await this.session!.run({ input: inputTensor })
119
165
 
120
- logger.start('Process MDXNet model output')
166
+ if (enableTraceLogging) {
167
+ await logger.startAsync('Reshape processed frames')
168
+ }
121
169
 
122
170
  const flattenedOutputTensor = outputTensor.data as Float32Array
123
171
 
124
- const outputChannelComplexFrames: ComplexNumber[][][] = []
172
+ const outputSegmentFramesForChannel: Float32Array[][] = []
125
173
 
126
174
  {
127
175
  for (let outChannelIndex = 0; outChannelIndex < 2; outChannelIndex++) {
128
- const framesForChannel: ComplexNumber[][] = []
176
+ const framesForChannel: Float32Array[] = []
129
177
 
130
- for (let frameIndex = 0; frameIndex < 256; frameIndex++) {
131
- const frame: ComplexNumber[] = []
132
-
133
- for (let binIndex = 0; binIndex < fftSize; binIndex++) {
134
- frame.push({ real: 0, imaginary: 0 })
135
- }
178
+ for (let frameIndex = 0; frameIndex < segmentSize; frameIndex++) {
179
+ const frame = new Float32Array(fftSize)
136
180
 
137
181
  framesForChannel.push(frame)
138
182
  }
139
183
 
140
- outputChannelComplexFrames.push(framesForChannel)
184
+ outputSegmentFramesForChannel.push(framesForChannel)
141
185
  }
142
186
 
143
187
  let readPosition = 0
144
188
 
145
189
  for (let tensorChannelIndex = 0; tensorChannelIndex < 4; tensorChannelIndex++) {
146
- const isEvenTensorChannelIndex = tensorChannelIndex % 2 === 0
147
- const outChannelIndex = tensorChannelIndex < 2 ? 0 : 1
190
+ const isRealTensorChannelIndex = tensorChannelIndex % 2 === 0
191
+ const audioChannelIndex = tensorChannelIndex < 2 ? 0 : 1
148
192
 
149
- const binsForOutputChannel = outputChannelComplexFrames[outChannelIndex]
193
+ const framesForOutputChannel = outputSegmentFramesForChannel[audioChannelIndex]
150
194
 
151
- for (let binIndex = 0; binIndex < 2048; binIndex++) {
152
- for (let frameIndex = 0; frameIndex < 256; frameIndex++) {
153
- const bin = binsForOutputChannel[frameIndex][binIndex]
195
+ for (let binIndex = 0; binIndex < binCount; binIndex++) {
196
+ for (let frameIndex = 0; frameIndex < segmentSize; frameIndex++) {
197
+ const outFrame = framesForOutputChannel[frameIndex]
154
198
 
155
- if (isEvenTensorChannelIndex) {
156
- bin.real = flattenedOutputTensor[readPosition++]
199
+ if (isRealTensorChannelIndex) {
200
+ outFrame[binIndex << 1] = flattenedOutputTensor[readPosition++] * fftSizeReciprocal
157
201
  } else {
158
- bin.imaginary = flattenedOutputTensor[readPosition++]
202
+ outFrame[(binIndex << 1) + 1] = flattenedOutputTensor[readPosition++] * fftSizeReciprocal
159
203
  }
160
204
  }
161
205
  }
@@ -164,29 +208,31 @@ export class MDXNet {
164
208
 
165
209
  const outputAudioChannels: Float32Array[] = []
166
210
 
167
- logger.start(`Compute inverse STFT for segment`)
168
- for (let channelIndex = 0; channelIndex < 2; channelIndex++) {
169
- const fftSizeReciprocal = 1 / fftSize
170
-
171
- let outputChannelFlattenedFrames = outputChannelComplexFrames[channelIndex]
172
- .map(frame => complexToBinBuffer(frame).map(value => value * fftSizeReciprocal))
211
+ if (enableTraceLogging) {
212
+ await logger.startAsync(`Compute inverse STFT of model output for segment`)
213
+ }
173
214
 
215
+ for (let channelIndex = 0; channelIndex < 2; channelIndex++) {
174
216
  const samples = await stiftr(
175
- outputChannelFlattenedFrames,
217
+ outputSegmentFramesForChannel[channelIndex],
176
218
  fftSize,
177
219
  fftWindowSize,
178
220
  fftHopSize,
179
- 'hann')
221
+ fftWindowType)
180
222
 
181
223
  outputAudioChannels.push(samples)
182
224
  }
183
225
 
184
226
  audioForSegments.push(outputAudioChannels)
227
+
228
+ if (isLastSegment) {
229
+ break
230
+ }
185
231
  }
186
232
 
187
233
  // Join segments using overlapping Hann windows
188
- logger.start(`Join segments`)
189
- const concatenatedAudioChannels = [new Float32Array(sampleCount), new Float32Array(sampleCount)]
234
+ await logger.startAsync(`Join segments`)
235
+ const joinedSegments = [new Float32Array(sampleCount), new Float32Array(sampleCount)]
190
236
 
191
237
  {
192
238
  const segmentCount = audioForSegments.length
@@ -213,7 +259,7 @@ export class MDXNet {
213
259
  const weight = windowWeights[segmentSampleOffset]
214
260
 
215
261
  for (let channelIndex = 0; channelIndex < 2; channelIndex++) {
216
- concatenatedAudioChannels[channelIndex][sampleIndex] += segmentSamples[channelIndex][segmentSampleOffset] * weight
262
+ joinedSegments[channelIndex][sampleIndex] += segmentSamples[channelIndex][segmentSampleOffset] * weight
217
263
  }
218
264
 
219
265
  sumOfWeightsForSample[sampleIndex] += weight
@@ -222,12 +268,12 @@ export class MDXNet {
222
268
 
223
269
  for (let sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) {
224
270
  for (let channelIndex = 0; channelIndex < 2; channelIndex++) {
225
- concatenatedAudioChannels[channelIndex][sampleIndex] /= sumOfWeightsForSample[sampleIndex] + 1e-8
271
+ joinedSegments[channelIndex][sampleIndex] /= sumOfWeightsForSample[sampleIndex] + 1e-8
226
272
  }
227
273
  }
228
274
  }
229
275
 
230
- const isolatedRawAudio: RawAudio = { audioChannels: concatenatedAudioChannels, sampleRate }
276
+ const isolatedRawAudio: RawAudio = { audioChannels: joinedSegments, sampleRate }
231
277
 
232
278
  logger.end()
233
279
 
@@ -241,8 +287,92 @@ export class MDXNet {
241
287
 
242
288
  const Onnx = await import('onnxruntime-node')
243
289
 
244
- const onnxSessionOptions = getOnnxSessionOptions({ executionProviders: this.executionProviders })
290
+ const executionProviders: OnnxExecutionProvider[] =
291
+ this.options.provider ? [this.options.provider] : getDefaultMDXNetProviders()
292
+
293
+ this.onnxSessionOptions = getOnnxSessionOptions({ executionProviders })
294
+
295
+ this.session = await Onnx.InferenceSession.create(this.modelFilePath, this.onnxSessionOptions)
296
+ }
297
+ }
245
298
 
246
- this.session = await Onnx.InferenceSession.create(this.modelFilePath, onnxSessionOptions)
299
+ export function getDefaultMDXNetProviders(): OnnxExecutionProvider[] {
300
+ if (dmlProviderAvailable()) {
301
+ return ['dml', 'cpu']
302
+ } else {
303
+ return []
247
304
  }
248
305
  }
306
+
307
+ export function getProfileForMDXNetModelName(modelName: MDXNetModelName) {
308
+ if (['UVR_MDXNET_1_9703', 'UVR_MDXNET_2_9682', 'UVR_MDXNET_3_9662', 'UVR_MDXNET_KARA'].includes(modelName)) {
309
+ return mdxNetModelProfile1
310
+ }
311
+
312
+ if (['UVR_MDXNET_Main', 'Kim_Vocal_1', 'Kim_Vocal_2'].includes(modelName)) {
313
+ return mdxNetModelProfile2
314
+ }
315
+
316
+ throw new Error(`Unsupported model name: '${modelName}'`)
317
+ }
318
+
319
+ export const mdxNetModelProfile1: MDXNetModelProfile = {
320
+ sampleRate: 44100,
321
+
322
+ fftSize: 6144,
323
+ fftWindowSize: 6144,
324
+ fftHopSize: 1024,
325
+ fftWindowType: 'hann',
326
+
327
+ binCount: 2048,
328
+
329
+ segmentSize: 256,
330
+ segmentHopSize: 224,
331
+ }
332
+
333
+ export const mdxNetModelProfile2: MDXNetModelProfile = {
334
+ sampleRate: 44100,
335
+
336
+ fftSize: 7680,
337
+ fftWindowSize: 7680,
338
+ fftHopSize: 1024,
339
+ fftWindowType: 'hann',
340
+
341
+ binCount: 3072,
342
+
343
+ segmentSize: 256,
344
+ segmentHopSize: 224,
345
+ }
346
+
347
+ export interface MDXNetModelProfile {
348
+ sampleRate: number
349
+
350
+ fftSize: number
351
+ fftWindowSize: number
352
+ fftHopSize: number
353
+ fftWindowType: WindowType
354
+
355
+ binCount: number
356
+
357
+ segmentSize: number
358
+ segmentHopSize: number
359
+ }
360
+
361
+ export type MDXNetModelName =
362
+ 'UVR_MDXNET_1_9703' |
363
+ 'UVR_MDXNET_2_9682' |
364
+ 'UVR_MDXNET_3_9662' |
365
+ 'UVR_MDXNET_KARA' |
366
+ 'UVR_MDXNET_Main' |
367
+ 'Kim_Vocal_1' |
368
+ 'Kim_Vocal_2'
369
+
370
+ export interface MDXNetOptions {
371
+ model?: MDXNetModelName
372
+ provider?: OnnxExecutionProvider
373
+ }
374
+
375
+ export const defaultMDXNetOptions: MDXNetOptions = {
376
+ model: 'UVR_MDXNET_1_9703',
377
+ provider: undefined,
378
+ }
@@ -1,11 +1,12 @@
1
1
  import { htmlToText } from 'html-to-text'
2
2
 
3
- import { formatHMS, formatMS, secondsToHMS, secondsToMS, startsWithAnyOf } from '../utilities/Utilities.js'
3
+ import { secondsToHMS, secondsToMS } from '../utilities/Utilities.js'
4
4
  import { isWord, isWordOrSymbolWord } from '../nlp/Segmentation.js'
5
5
  import { charactersToWriteAhead } from '../audio/AudioPlayer.js'
6
6
  import { Timeline, TimelineEntry } from '../utilities/Timeline.js'
7
7
  import { readFile } from '../utilities/FileSystem.js'
8
8
  import { deepClone } from '../utilities/ObjectUtilities.js'
9
+ import { formatHMS, formatMS, startsWithAnyOf } from '../utilities/StringUtilities.js'
9
10
 
10
11
  export async function subtitlesFileToText(filename: string) {
11
12
  return subtitlesToText(await readFile(filename, 'utf8'))
@@ -1,10 +1,10 @@
1
- import { concatFloat32Arrays, logToStderr, objToString, simplifyPunctuationCharacters } from '../utilities/Utilities.js'
1
+ import { concatFloat32Arrays, logToStderr, formatObjectToString } from '../utilities/Utilities.js'
2
2
  import { int16PcmToFloat32 } from '../audio/AudioBufferConversion.js'
3
3
  import { Logger } from '../utilities/Logger.js'
4
4
  import { WasmMemoryManager } from '../utilities/WasmMemoryManager.js'
5
5
  import { RawAudio, getEmptyRawAudio } from '../audio/AudioUtilities.js'
6
6
  import { playAudioWithTimelinePhones } from '../audio/AudioPlayer.js'
7
- import { getNormalizedFragmentsForSpeech } from '../nlp/TextNormalizer.js'
7
+ import { getNormalizedFragmentsForSpeech, simplifyPunctuationCharacters } from '../nlp/TextNormalizer.js'
8
8
  import { ipaPhoneToKirshenbaum } from '../nlp/PhoneConversion.js'
9
9
  import { splitToWords, wordCharacterPattern } from '../nlp/Segmentation.js'
10
10
  import { Lexicon, tryGetFirstLexiconSubstitution } from '../nlp/Lexicon.js'
@@ -276,11 +276,11 @@ export async function synthesizeFragments(fragments: string[], espeakOptions: Es
276
276
  const markerIndex = parseInt(markerName.substring(2))
277
277
 
278
278
  if (markerIndex != wordIndex) {
279
- throw new Error(`Word start marker for index ${wordIndex} is not consistent with word index. The words were: ${objToString(fragments)}`)
279
+ throw new Error(`Word start marker for index ${wordIndex} is not consistent with word index. The words were: ${formatObjectToString(fragments)}`)
280
280
  }
281
281
 
282
282
  if (currentPhoneTimeline.length > 0) {
283
- throw new Error(`Word entry ${wordIndex} already has phones before its start marker was seen. The words were: ${objToString(fragments)}`)
283
+ throw new Error(`Word entry ${wordIndex} already has phones before its start marker was seen. The words were: ${formatObjectToString(fragments)}`)
284
284
  }
285
285
 
286
286
  currentWordEntry.startTime = eventTime
@@ -289,7 +289,7 @@ export async function synthesizeFragments(fragments: string[], espeakOptions: Es
289
289
  const markerIndex = parseInt(markerName.substring(2))
290
290
 
291
291
  if (markerIndex != wordIndex) {
292
- throw new Error(`Word end marker for index ${wordIndex} is not consistent with word index. The words were: ${objToString(fragments)}`)
292
+ throw new Error(`Word end marker for index ${wordIndex} is not consistent with word index. The words were: ${formatObjectToString(fragments)}`)
293
293
  }
294
294
 
295
295
  currentWordEntry.startTime = currentTokenTimeline[0].startTime
package/src/tests/Test.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { makeTarballsForInstalledPackages } from '../utilities/TarballMaker.js'
2
1
  import { logToStderr, setupProgramTerminationListeners } from '../utilities/Utilities.js'
3
2
 
4
3
  const log = logToStderr
@@ -8,7 +7,4 @@ setupProgramTerminationListeners()
8
7
  //process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
9
8
  //process.env.http_proxy = 'http://localhost:8080'
10
9
 
11
- //await makeTarballsForInstalledPackages(true)
12
-
13
10
  process.exit(0)
14
-