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
|
@@ -1,107 +1,151 @@
|
|
|
1
1
|
import type * as Onnx from 'onnxruntime-node'
|
|
2
|
-
import { RawAudio } from '../audio/AudioUtilities.js'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
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
|
-
|
|
16
|
+
modelProfile: MDXNetModelProfile,
|
|
17
|
+
options: MDXNetOptions) {
|
|
15
18
|
|
|
16
|
-
const model = new MDXNet(modelFilePath,
|
|
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
|
|
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
|
|
35
|
+
if (rawAudio.audioChannels.length !== 2) {
|
|
31
36
|
throw new Error(`Input audio must be stereo`)
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
if (rawAudio.sampleRate
|
|
35
|
-
throw new Error(`Input audio must have a sample rate of
|
|
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
|
-
|
|
43
|
+
if (rawAudio.audioChannels[0].length === 0) {
|
|
44
|
+
return getEmptyRawAudio(rawAudio.audioChannels.length, rawAudio.sampleRate)
|
|
45
|
+
}
|
|
39
46
|
|
|
40
|
-
const
|
|
47
|
+
const enableTraceLogging = logLevelGreaterOrEqualTo('trace')
|
|
41
48
|
|
|
42
49
|
const logger = new Logger()
|
|
43
50
|
|
|
44
|
-
|
|
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
|
|
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 =
|
|
53
|
-
const segmentHopSize =
|
|
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
|
-
|
|
74
|
+
const fftSizeReciprocal = 1 / fftSize
|
|
58
75
|
|
|
59
|
-
|
|
60
|
-
const
|
|
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
|
-
|
|
63
|
-
const
|
|
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
|
|
68
|
-
const
|
|
86
|
+
for (let segmentStartFrameOffset = 0; ; segmentStartFrameOffset += segmentHopSize) {
|
|
87
|
+
const segmentEndFrameOffset = segmentStartFrameOffset + segmentSize
|
|
69
88
|
|
|
70
|
-
|
|
89
|
+
const timePosition = segmentStartFrameOffset * (fftHopSize / sampleRate)
|
|
71
90
|
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
97
|
+
while (fftFramesLeftWindowedList.endOffset < segmentEndFrameOffset) {
|
|
98
|
+
const nextLeftFrameResult = await fftFramesLeftGenerator.next()
|
|
99
|
+
|
|
100
|
+
if (nextLeftFrameResult.done) {
|
|
101
|
+
break
|
|
102
|
+
}
|
|
76
103
|
|
|
77
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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 <
|
|
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
|
|
91
|
-
|
|
142
|
+
if (frameIndex < segmentLength) {
|
|
143
|
+
const frame = fftFramesForSegment[audioChannelIndex][frameIndex]
|
|
92
144
|
|
|
93
|
-
if (
|
|
94
|
-
|
|
145
|
+
if (isRealComponentTensorElementIndex) {
|
|
146
|
+
value = frame[binIndex << 1]
|
|
95
147
|
} else {
|
|
96
|
-
|
|
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
|
-
|
|
158
|
+
if (enableTraceLogging) {
|
|
159
|
+
await logger.startAsync(`Process segment with MDXNet model`)
|
|
160
|
+
}
|
|
115
161
|
|
|
116
|
-
|
|
162
|
+
const inputTensor = new Onnx.Tensor('float32', flattenedInputTensor, [1, 4, binCount, segmentSize])
|
|
117
163
|
|
|
118
|
-
const { output: outputTensor } = await session
|
|
164
|
+
const { output: outputTensor } = await this.session!.run({ input: inputTensor })
|
|
119
165
|
|
|
120
|
-
|
|
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
|
|
172
|
+
const outputSegmentFramesForChannel: Float32Array[][] = []
|
|
125
173
|
|
|
126
174
|
{
|
|
127
175
|
for (let outChannelIndex = 0; outChannelIndex < 2; outChannelIndex++) {
|
|
128
|
-
const framesForChannel:
|
|
176
|
+
const framesForChannel: Float32Array[] = []
|
|
129
177
|
|
|
130
|
-
for (let frameIndex = 0; frameIndex <
|
|
131
|
-
const frame
|
|
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
|
-
|
|
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
|
|
147
|
-
const
|
|
190
|
+
const isRealTensorChannelIndex = tensorChannelIndex % 2 === 0
|
|
191
|
+
const audioChannelIndex = tensorChannelIndex < 2 ? 0 : 1
|
|
148
192
|
|
|
149
|
-
const
|
|
193
|
+
const framesForOutputChannel = outputSegmentFramesForChannel[audioChannelIndex]
|
|
150
194
|
|
|
151
|
-
for (let binIndex = 0; binIndex <
|
|
152
|
-
for (let frameIndex = 0; frameIndex <
|
|
153
|
-
const
|
|
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 (
|
|
156
|
-
|
|
199
|
+
if (isRealTensorChannelIndex) {
|
|
200
|
+
outFrame[binIndex << 1] = flattenedOutputTensor[readPosition++] * fftSizeReciprocal
|
|
157
201
|
} else {
|
|
158
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
217
|
+
outputSegmentFramesForChannel[channelIndex],
|
|
176
218
|
fftSize,
|
|
177
219
|
fftWindowSize,
|
|
178
220
|
fftHopSize,
|
|
179
|
-
|
|
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.
|
|
189
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
271
|
+
joinedSegments[channelIndex][sampleIndex] /= sumOfWeightsForSample[sampleIndex] + 1e-8
|
|
226
272
|
}
|
|
227
273
|
}
|
|
228
274
|
}
|
|
229
275
|
|
|
230
|
-
const isolatedRawAudio: RawAudio = { audioChannels:
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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,
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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
|
-
|