echogarden 2.6.0 → 2.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/lexicons/heteronyms.en.json +6 -2
- package/data/lexicons/words.en.json +379 -19
- package/dist/alignment/SemanticTextAlignment.js +6 -7
- package/dist/alignment/SemanticTextAlignment.js.map +1 -1
- package/dist/api/Alignment.js +2 -2
- package/dist/api/Alignment.js.map +1 -1
- package/dist/api/Recognition.js +2 -2
- package/dist/api/Recognition.js.map +1 -1
- package/dist/api/SourceSeparation.js +1 -1
- package/dist/api/SpeechTranslation.js +2 -2
- package/dist/api/SpeechTranslation.js.map +1 -1
- package/dist/api/Synthesis.js +7 -7
- package/dist/api/Synthesis.js.map +1 -1
- package/dist/api/TimelineTranslationAlignment.js +2 -2
- package/dist/api/TimelineTranslationAlignment.js.map +1 -1
- package/dist/api/TranslationAlignment.js +2 -2
- package/dist/api/TranslationAlignment.js.map +1 -1
- package/dist/audio/AudioBufferConversion.d.ts +5 -6
- package/dist/audio/AudioBufferConversion.js +16 -248
- package/dist/audio/AudioBufferConversion.js.map +1 -1
- package/dist/audio/AudioPlayer.js +2 -2
- package/dist/audio/AudioPlayer.js.map +1 -1
- package/dist/audio/AudioRecorder.js +1 -1
- package/dist/audio/AudioRecorder.js.map +1 -1
- package/dist/audio/AudioUtilities.d.ts +2 -8
- package/dist/audio/AudioUtilities.js +4 -3
- package/dist/audio/AudioUtilities.js.map +1 -1
- package/dist/cli/CLI.js +4 -4
- package/dist/cli/CLI.js.map +1 -1
- package/dist/codecs/FFMpegTranscoder.d.ts +1 -4
- package/dist/codecs/TIMITCodec.js +1 -2
- package/dist/codecs/TIMITCodec.js.map +1 -1
- package/dist/denoising/RNNoise.js +5 -5
- package/dist/denoising/RNNoise.js.map +1 -1
- package/dist/dsp/FFT.js +13 -19
- package/dist/dsp/FFT.js.map +1 -1
- package/dist/dsp/Rubberband.js +5 -5
- package/dist/dsp/Rubberband.js.map +1 -1
- package/dist/dsp/Sonic.js +6 -6
- package/dist/dsp/Sonic.js.map +1 -1
- package/dist/dsp/SpeexResampler.js +11 -11
- package/dist/dsp/SpeexResampler.js.map +1 -1
- package/dist/encodings/Ascii.js +1 -1
- package/dist/encodings/Ascii.js.map +1 -1
- package/dist/encodings/Utf16.js +1 -1
- package/dist/encodings/Utf16.js.map +1 -1
- package/dist/encodings/Utf8.js +1 -1
- package/dist/encodings/Utf8.js.map +1 -1
- package/dist/nlp/EspeakPhonemizer.js +1 -1
- package/dist/nlp/EspeakPhonemizer.js.map +1 -1
- package/dist/nlp/Segmentation.d.ts +13 -10
- package/dist/nlp/Segmentation.js +129 -106
- package/dist/nlp/Segmentation.js.map +1 -1
- package/dist/nlp/TextNormalizer.js +74 -42
- package/dist/nlp/TextNormalizer.js.map +1 -1
- package/dist/recognition/AmazonTranscribeSTT.js +2 -2
- package/dist/recognition/AmazonTranscribeSTT.js.map +1 -1
- package/dist/recognition/SileroSTT.js +2 -2
- package/dist/recognition/SileroSTT.js.map +1 -1
- package/dist/recognition/WhisperSTT.js +4 -16
- package/dist/recognition/WhisperSTT.js.map +1 -1
- package/dist/subtitles/Subtitles.js +52 -7
- package/dist/subtitles/Subtitles.js.map +1 -1
- package/dist/synthesis/AwsPollyTTS.d.ts +1 -4
- package/dist/synthesis/CoquiServerTTS.d.ts +1 -4
- package/dist/synthesis/DeepgramTTS.d.ts +1 -4
- package/dist/synthesis/ElevenLabsTTS.d.ts +1 -4
- package/dist/synthesis/ElevenLabsTTS.js +11 -10
- package/dist/synthesis/ElevenLabsTTS.js.map +1 -1
- package/dist/synthesis/EspeakTTS.js +8 -8
- package/dist/synthesis/EspeakTTS.js.map +1 -1
- package/dist/synthesis/FliteTTS.d.ts +1 -4
- package/dist/synthesis/GnuSpeechTTS.d.ts +1 -7
- package/dist/synthesis/GoogleCloudTTS.d.ts +1 -4
- package/dist/synthesis/KokoroTTS.js +21 -10
- package/dist/synthesis/KokoroTTS.js.map +1 -1
- package/dist/synthesis/SapiTTS.js +1 -2
- package/dist/synthesis/SapiTTS.js.map +1 -1
- package/dist/synthesis/SvoxPicoTTS.js +25 -25
- package/dist/synthesis/SvoxPicoTTS.js.map +1 -1
- package/dist/text-translation/DeepLTextTranslation.js +4 -3
- package/dist/text-translation/DeepLTextTranslation.js.map +1 -1
- package/dist/text-translation/NLLBTextTranslation.js +7 -6
- package/dist/text-translation/NLLBTextTranslation.js.map +1 -1
- package/dist/utilities/StringUtilities.d.ts +1 -1
- package/dist/utilities/StringUtilities.js +5 -5
- package/dist/utilities/StringUtilities.js.map +1 -1
- package/dist/utilities/Timeline.d.ts +2 -3
- package/dist/utilities/Timeline.js +96 -147
- package/dist/utilities/Timeline.js.map +1 -1
- package/dist/utilities/WikipediaReader.js +3 -3
- package/dist/utilities/WikipediaReader.js.map +1 -1
- package/dist/voice-activity-detection/WebRtcVAD.js +4 -4
- package/dist/voice-activity-detection/WebRtcVAD.js.map +1 -1
- package/docs/Tasklist.md +1 -6
- package/package.json +13 -11
- package/src/alignment/SemanticTextAlignment.ts +6 -7
- package/src/api/Alignment.ts +2 -2
- package/src/api/Recognition.ts +2 -2
- package/src/api/SourceSeparation.ts +1 -1
- package/src/api/SpeechTranslation.ts +2 -2
- package/src/api/Synthesis.ts +7 -7
- package/src/api/TimelineTranslationAlignment.ts +2 -2
- package/src/api/TranslationAlignment.ts +2 -2
- package/src/audio/AudioBufferConversion.ts +17 -263
- package/src/audio/AudioPlayer.ts +3 -3
- package/src/audio/AudioRecorder.ts +1 -1
- package/src/audio/AudioUtilities.ts +5 -3
- package/src/cli/CLI.ts +4 -4
- package/src/codecs/TIMITCodec.ts +1 -2
- package/src/denoising/RNNoise.ts +5 -5
- package/src/dsp/FFT.ts +22 -19
- package/src/dsp/Rubberband.ts +5 -5
- package/src/dsp/Sonic.ts +6 -6
- package/src/dsp/SpeexResampler.ts +11 -11
- package/src/encodings/Ascii.ts +1 -1
- package/src/encodings/Utf16.ts +1 -1
- package/src/encodings/Utf8.ts +1 -1
- package/src/nlp/EspeakPhonemizer.ts +1 -1
- package/src/nlp/Segmentation.ts +146 -111
- package/src/nlp/TextNormalizer.ts +94 -52
- package/src/recognition/AmazonTranscribeSTT.ts +2 -2
- package/src/recognition/SileroSTT.ts +2 -2
- package/src/recognition/WhisperSTT.ts +4 -18
- package/src/subtitles/Subtitles.ts +69 -8
- package/src/synthesis/ElevenLabsTTS.ts +12 -11
- package/src/synthesis/EspeakTTS.ts +9 -8
- package/src/synthesis/KokoroTTS.ts +24 -12
- package/src/synthesis/SapiTTS.ts +1 -2
- package/src/synthesis/SvoxPicoTTS.ts +27 -25
- package/src/text-translation/DeepLTextTranslation.ts +4 -3
- package/src/text-translation/NLLBTextTranslation.ts +8 -6
- package/src/typings/Fillers.d.ts +0 -16
- package/src/utilities/StringUtilities.ts +5 -6
- package/src/utilities/Timeline.ts +100 -170
- package/src/utilities/WikipediaReader.ts +3 -3
- package/src/voice-activity-detection/WebRtcVAD.ts +5 -4
- package/dist/codecs/WaveCodec.d.ts +0 -19
- package/dist/codecs/WaveCodec.js +0 -224
- package/dist/codecs/WaveCodec.js.map +0 -1
- package/dist/nlp/CompromiseNLP.d.ts +0 -15
- package/dist/nlp/CompromiseNLP.js +0 -79
- package/dist/nlp/CompromiseNLP.js.map +0 -1
- package/dist/utilities/BinaryArrayConversion.d.ts +0 -14
- package/dist/utilities/BinaryArrayConversion.js +0 -92
- package/dist/utilities/BinaryArrayConversion.js.map +0 -1
- package/dist/utilities/WasmMemoryManager.d.ts +0 -153
- package/dist/utilities/WasmMemoryManager.js +0 -440
- package/dist/utilities/WasmMemoryManager.js.map +0 -1
- package/src/codecs/WaveCodec.ts +0 -281
- package/src/nlp/CompromiseNLP.ts +0 -119
- package/src/utilities/BinaryArrayConversion.ts +0 -117
- package/src/utilities/WasmMemoryManager.ts +0 -566
|
@@ -1,297 +1,51 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import
|
|
3
|
-
import { BitDepth, SampleFormat } from '../codecs/WaveCodec.js'
|
|
1
|
+
import * as WaveCodec from '@echogarden/wave-codec'
|
|
2
|
+
import { BitDepth, SampleFormat } from '@echogarden/wave-codec'
|
|
4
3
|
|
|
5
4
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
6
5
|
// Low level audio sample conversions
|
|
7
6
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
8
7
|
export function encodeToAudioBuffer(audioChannels: Float32Array[], targetBitDepth: BitDepth = 16, targetSampleFormat: SampleFormat = SampleFormat.PCM): Uint8Array {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
audioChannels = [] // Zero the array references to allow the GC to free up memory, if possible
|
|
12
|
-
|
|
13
|
-
if (targetSampleFormat === SampleFormat.PCM) {
|
|
14
|
-
if (targetBitDepth === 8) {
|
|
15
|
-
return BinaryArrayConversion.int8ToBuffer(float32ToInt8Pcm(interleavedChannels))
|
|
16
|
-
} else if (targetBitDepth === 16) {
|
|
17
|
-
return BinaryArrayConversion.int16ToBufferLE(float32ToInt16Pcm(interleavedChannels))
|
|
18
|
-
} else if (targetBitDepth === 24) {
|
|
19
|
-
return BinaryArrayConversion.int24ToBufferLE(float32ToInt24Pcm(interleavedChannels))
|
|
20
|
-
} else if (targetBitDepth === 32) {
|
|
21
|
-
return BinaryArrayConversion.int32ToBufferLE(float32ToInt32Pcm(interleavedChannels))
|
|
22
|
-
} else {
|
|
23
|
-
throw new Error(`Unsupported PCM bit depth: ${targetBitDepth}`)
|
|
24
|
-
}
|
|
25
|
-
} else if (targetSampleFormat === SampleFormat.Float) {
|
|
26
|
-
if (targetBitDepth === 32) {
|
|
27
|
-
return BinaryArrayConversion.float32ToBufferLE(interleavedChannels)
|
|
28
|
-
} else if (targetBitDepth === 64) {
|
|
29
|
-
return BinaryArrayConversion.float64ToBufferLE(BinaryArrayConversion.float32Tofloat64(interleavedChannels))
|
|
30
|
-
} else {
|
|
31
|
-
throw new Error(`Unsupported float bit depth: ${targetBitDepth}`)
|
|
32
|
-
}
|
|
33
|
-
} else if (targetSampleFormat === SampleFormat.Alaw) {
|
|
34
|
-
if (targetBitDepth === 8) {
|
|
35
|
-
return AlawMulaw.alaw.encode(float32ToInt16Pcm(interleavedChannels))
|
|
36
|
-
} else {
|
|
37
|
-
throw new Error(`Unsupported alaw bit depth: ${targetBitDepth}`)
|
|
38
|
-
}
|
|
39
|
-
} else if (targetSampleFormat === SampleFormat.Mulaw) {
|
|
40
|
-
if (targetBitDepth === 8) {
|
|
41
|
-
return AlawMulaw.mulaw.encode(float32ToInt16Pcm(interleavedChannels))
|
|
42
|
-
} else {
|
|
43
|
-
throw new Error(`Unsupported mulaw bit depth: ${targetBitDepth}`)
|
|
44
|
-
}
|
|
45
|
-
} else {
|
|
46
|
-
throw new Error(`Unsupported audio format: ${targetSampleFormat}`)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function decodeToChannels(audioBuffer: Uint8Array, channelCount: number, sourceBitDepth: number, sourceSampleFormat: SampleFormat) {
|
|
51
|
-
let interleavedChannels: Float32Array
|
|
52
|
-
|
|
53
|
-
if (sourceSampleFormat === SampleFormat.PCM) {
|
|
54
|
-
if (sourceBitDepth === 8) {
|
|
55
|
-
interleavedChannels = int8PcmToFloat32(BinaryArrayConversion.bufferToInt8(audioBuffer))
|
|
56
|
-
} else if (sourceBitDepth === 16) {
|
|
57
|
-
interleavedChannels = int16PcmToFloat32(BinaryArrayConversion.bufferLEToInt16(audioBuffer))
|
|
58
|
-
} else if (sourceBitDepth === 24) {
|
|
59
|
-
interleavedChannels = int24PcmToFloat32(BinaryArrayConversion.bufferLEToInt24(audioBuffer))
|
|
60
|
-
} else if (sourceBitDepth === 32) {
|
|
61
|
-
interleavedChannels = int32PcmToFloat32(BinaryArrayConversion.bufferLEToInt32(audioBuffer))
|
|
62
|
-
} else {
|
|
63
|
-
throw new Error(`Unsupported PCM bit depth: ${sourceBitDepth}`)
|
|
64
|
-
}
|
|
65
|
-
} else if (sourceSampleFormat === SampleFormat.Float) {
|
|
66
|
-
if (sourceBitDepth === 32) {
|
|
67
|
-
interleavedChannels = BinaryArrayConversion.bufferLEToFloat32(audioBuffer)
|
|
68
|
-
} else if (sourceBitDepth === 64) {
|
|
69
|
-
interleavedChannels = BinaryArrayConversion.float64Tofloat32(BinaryArrayConversion.bufferLEToFloat64(audioBuffer))
|
|
70
|
-
} else {
|
|
71
|
-
throw new Error(`Unsupported float bit depth: ${sourceBitDepth}`)
|
|
72
|
-
}
|
|
73
|
-
} else if (sourceSampleFormat === SampleFormat.Alaw) {
|
|
74
|
-
if (sourceBitDepth === 8) {
|
|
75
|
-
interleavedChannels = int16PcmToFloat32(AlawMulaw.alaw.decode(audioBuffer))
|
|
76
|
-
} else {
|
|
77
|
-
throw new Error(`Unsupported alaw bit depth: ${sourceBitDepth}`)
|
|
78
|
-
}
|
|
79
|
-
} else if (sourceSampleFormat === SampleFormat.Mulaw) {
|
|
80
|
-
if (sourceBitDepth === 8) {
|
|
81
|
-
interleavedChannels = int16PcmToFloat32(AlawMulaw.mulaw.decode(audioBuffer))
|
|
82
|
-
} else {
|
|
83
|
-
throw new Error(`Unsupported mulaw bit depth: ${sourceBitDepth}`)
|
|
84
|
-
}
|
|
85
|
-
} else {
|
|
86
|
-
throw new Error(`Unsupported audio format: ${sourceSampleFormat}`)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
audioBuffer = new Uint8Array(0) // Zero the buffer reference to allow the GC to free up memory, if possible
|
|
90
|
-
|
|
91
|
-
return deInterleaveChannels(interleavedChannels, channelCount)
|
|
8
|
+
return WaveCodec.float32ChannelsToBuffer(audioChannels, targetBitDepth, targetSampleFormat)
|
|
92
9
|
}
|
|
93
10
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const sampleCount = input.length
|
|
97
|
-
|
|
98
|
-
const output = new Float32Array(sampleCount)
|
|
99
|
-
|
|
100
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
101
|
-
output[i] = input[i] / 128
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return output
|
|
11
|
+
export function decodeToChannels(audioBuffer: Uint8Array, channelCount: number, sourceBitDepth: BitDepth, sourceSampleFormat: SampleFormat) {
|
|
12
|
+
return WaveCodec.bufferToFloat32Channels(audioBuffer, channelCount, sourceBitDepth, sourceSampleFormat)
|
|
105
13
|
}
|
|
106
14
|
|
|
107
|
-
export function
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const output = new Int8Array(sampleCount)
|
|
111
|
-
|
|
112
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
113
|
-
const int8Sample = input[i] * 128
|
|
114
|
-
|
|
115
|
-
if (int8Sample < -128) {
|
|
116
|
-
output[i] = -128
|
|
117
|
-
} else if (int8Sample > 127) {
|
|
118
|
-
output[i] = 127
|
|
119
|
-
} else {
|
|
120
|
-
output[i] = int8Sample
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return output
|
|
15
|
+
export function float32ToUint8Pcm(input: Float32Array) {
|
|
16
|
+
return WaveCodec.float32ToUint8Pcm(input)
|
|
125
17
|
}
|
|
126
18
|
|
|
127
|
-
// Int16 PCM <-> Float32 conversion
|
|
128
19
|
export function int16PcmToFloat32(input: Int16Array) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const output = new Float32Array(sampleCount)
|
|
132
|
-
|
|
133
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
134
|
-
output[i] = input[i] / 32768
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return output
|
|
20
|
+
return WaveCodec.int16PcmToFloat32(input)
|
|
138
21
|
}
|
|
139
22
|
|
|
140
23
|
export function float32ToInt16Pcm(input: Float32Array) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const output = new Int16Array(sampleCount)
|
|
144
|
-
|
|
145
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
146
|
-
const int16Sample = input[i] * 32768
|
|
147
|
-
|
|
148
|
-
if (int16Sample < -32768) {
|
|
149
|
-
output[i] = -32768
|
|
150
|
-
} else if (int16Sample > 32767) {
|
|
151
|
-
output[i] = 32767
|
|
152
|
-
} else {
|
|
153
|
-
output[i] = int16Sample
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return output
|
|
24
|
+
return WaveCodec.float32ToInt16Pcm(input)
|
|
158
25
|
}
|
|
159
26
|
|
|
160
|
-
// Int24 PCM <-> Float32 conversion (uses int32 for storage)
|
|
161
27
|
export function int24PcmToFloat32(input: Int32Array) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const output = new Float32Array(sampleCount)
|
|
165
|
-
|
|
166
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
167
|
-
output[i] = input[i] / 8388608
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return output
|
|
28
|
+
return WaveCodec.int24PcmToFloat32(input)
|
|
171
29
|
}
|
|
172
30
|
|
|
173
31
|
export function float32ToInt24Pcm(input: Float32Array) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const output = new Int32Array(sampleCount)
|
|
177
|
-
|
|
178
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
179
|
-
const int24Sample = input[i] * 8388608
|
|
180
|
-
|
|
181
|
-
if (int24Sample < -8388608) {
|
|
182
|
-
output[i] = -8388608
|
|
183
|
-
} else if (int24Sample > 8388607) {
|
|
184
|
-
output[i] = 8388607
|
|
185
|
-
} else {
|
|
186
|
-
output[i] = int24Sample
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return output
|
|
32
|
+
return WaveCodec.float32ToInt24Pcm(input)
|
|
191
33
|
}
|
|
192
34
|
|
|
193
|
-
// Int32 PCM <-> Float32 conversion
|
|
194
35
|
export function int32PcmToFloat32(input: Int32Array) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const output = new Float32Array(sampleCount)
|
|
198
|
-
|
|
199
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
200
|
-
output[i] = input[i] / 2147483648
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return output
|
|
36
|
+
return WaveCodec.int32PcmToFloat32(input)
|
|
204
37
|
}
|
|
205
38
|
|
|
206
39
|
export function float32ToInt32Pcm(input: Float32Array) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const output = new Int32Array(sampleCount)
|
|
210
|
-
|
|
211
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
212
|
-
const int32Sample = input[i] * 2147483648
|
|
213
|
-
|
|
214
|
-
if (int32Sample < -2147483648) {
|
|
215
|
-
output[i] = -2147483648
|
|
216
|
-
} else if (int32Sample > 2147483647) {
|
|
217
|
-
output[i] = 2147483647
|
|
218
|
-
} else {
|
|
219
|
-
output[i] = int32Sample
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
return output
|
|
40
|
+
return WaveCodec.float32ToInt32Pcm(input)
|
|
224
41
|
}
|
|
225
42
|
|
|
226
|
-
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
227
|
-
// Channel interleaving
|
|
228
|
-
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
229
43
|
export function interleaveChannels(channels: Float32Array[]) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (channelCount === 0) {
|
|
233
|
-
throw new Error('Empty channel array received')
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (channelCount === 1) {
|
|
237
|
-
return channels[0]
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const sampleCount = channels[0].length
|
|
241
|
-
const result = new Float32Array(sampleCount * channelCount)
|
|
242
|
-
|
|
243
|
-
let writeIndex = 0
|
|
244
|
-
|
|
245
|
-
for (let sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) {
|
|
246
|
-
for (let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
|
|
247
|
-
result[writeIndex++] = channels[channelIndex][sampleIndex]
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return result
|
|
44
|
+
return WaveCodec.interleaveChannels(channels)
|
|
252
45
|
}
|
|
253
46
|
|
|
254
|
-
export function
|
|
255
|
-
|
|
256
|
-
throw new Error('0 channel count received')
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (channelCount === 1) {
|
|
260
|
-
return [interleavedChannels]
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (interleavedChannels.length % channelCount != 0) {
|
|
264
|
-
throw new Error(`Size of interleaved channels (${interleaveChannels.length}) is not a multiple of channel count (${channelCount})`)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const sampleCount = interleavedChannels.length / channelCount
|
|
268
|
-
|
|
269
|
-
const channels: Float32Array[] = []
|
|
270
|
-
|
|
271
|
-
for (let i = 0; i < channelCount; i++) {
|
|
272
|
-
channels.push(new Float32Array(sampleCount))
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
let readIndex = 0
|
|
276
|
-
|
|
277
|
-
for (let sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) {
|
|
278
|
-
for (let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
|
|
279
|
-
channels[channelIndex][sampleIndex] = interleavedChannels[readIndex++]
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return channels
|
|
47
|
+
export function deinterleaveChannels(interleavedChannels: Float32Array, channelCount: number) {
|
|
48
|
+
return WaveCodec.deinterleaveChannels(interleavedChannels, channelCount)
|
|
284
49
|
}
|
|
285
50
|
|
|
286
|
-
|
|
287
|
-
// Utilities
|
|
288
|
-
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
289
|
-
export function clampFloatSample(floatSample: number) {
|
|
290
|
-
if (floatSample < -1.0) {
|
|
291
|
-
return -1.0
|
|
292
|
-
} else if (floatSample > 1.0) {
|
|
293
|
-
return 1.0
|
|
294
|
-
} else {
|
|
295
|
-
return floatSample
|
|
296
|
-
}
|
|
297
|
-
}
|
|
51
|
+
export { type BitDepth, SampleFormat } from '@echogarden/wave-codec'
|
package/src/audio/AudioPlayer.ts
CHANGED
|
@@ -2,14 +2,14 @@ import { parentPort } from 'node:worker_threads'
|
|
|
2
2
|
|
|
3
3
|
import { ChildProcessWithoutNullStreams, spawn } from 'child_process'
|
|
4
4
|
|
|
5
|
-
import { RawAudio, encodeRawAudioToWave, fadeAudioInOut, getRawAudioDuration, sliceAudioChannels
|
|
5
|
+
import { RawAudio, encodeRawAudioToWave, fadeAudioInOut, getRawAudioDuration, sliceAudioChannels } from './AudioUtilities.js'
|
|
6
6
|
import * as FFMpegTranscoder from '../codecs/FFMpegTranscoder.js'
|
|
7
7
|
|
|
8
8
|
import { Timer } from '../utilities/Timer.js'
|
|
9
9
|
import { clip, getRandomHexString, waitTimeout, writeToStderr } from '../utilities/Utilities.js'
|
|
10
10
|
import { encodeToAudioBuffer, float32ToInt16Pcm, interleaveChannels } from './AudioBufferConversion.js'
|
|
11
11
|
import { OpenPromise } from '../utilities/OpenPromise.js'
|
|
12
|
-
import { Timeline,
|
|
12
|
+
import { Timeline, addWordTextOffsetsToTimelineInPlace } from '../utilities/Timeline.js'
|
|
13
13
|
import { readAndParseJsonFile, readFileAsUtf8, remove, writeFile } from '../utilities/FileSystem.js'
|
|
14
14
|
import { tryResolvingSoxPath } from './SoxPath.js'
|
|
15
15
|
import { SignalChannel } from '../utilities/SignalChannel.js'
|
|
@@ -38,7 +38,7 @@ export async function playAudioWithWordTimeline(rawAudio: RawAudio, wordTimeline
|
|
|
38
38
|
|
|
39
39
|
wordTimeline = deepClone(wordTimeline)
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
addWordTextOffsetsToTimelineInPlace(wordTimeline, transcript)
|
|
42
42
|
|
|
43
43
|
let timelineEntryIndex = 0
|
|
44
44
|
let transcriptOffset = 0
|
|
@@ -6,8 +6,8 @@ import * as AudioBufferConversion from './AudioBufferConversion.js'
|
|
|
6
6
|
import { Timer } from '../utilities/Timer.js'
|
|
7
7
|
import { logToStderr } from '../utilities/Utilities.js'
|
|
8
8
|
import { OpenPromise } from '../utilities/OpenPromise.js'
|
|
9
|
-
import { SampleFormat } from '../codecs/WaveCodec.js'
|
|
10
9
|
import { tryResolvingSoxPath } from './SoxPath.js'
|
|
10
|
+
import { SampleFormat } from './AudioBufferConversion.js'
|
|
11
11
|
|
|
12
12
|
const log = logToStderr
|
|
13
13
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as FFMpegTranscoder from '../codecs/FFMpegTranscoder.js'
|
|
2
|
-
import { SampleFormat,
|
|
2
|
+
import { SampleFormat, BitDepth, encodeWaveFromFloat32Channels, decodeWaveToFloat32Channels } from '@echogarden/wave-codec'
|
|
3
3
|
import { resampleAudioSpeex } from '../dsp/SpeexResampler.js'
|
|
4
4
|
import { Timeline } from '../utilities/Timeline.js'
|
|
5
5
|
import { concatFloat32Arrays } from '../utilities/Utilities.js'
|
|
@@ -8,11 +8,13 @@ import { concatFloat32Arrays } from '../utilities/Utilities.js'
|
|
|
8
8
|
// Wave encoding and decoding
|
|
9
9
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
10
10
|
export function encodeRawAudioToWave(rawAudio: RawAudio, bitDepth: BitDepth = 16, sampleFormat: SampleFormat = SampleFormat.PCM, speakerPositionMask = 0) {
|
|
11
|
-
return
|
|
11
|
+
return encodeWaveFromFloat32Channels(rawAudio.audioChannels, rawAudio.sampleRate, bitDepth, sampleFormat, speakerPositionMask)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function decodeWaveToRawAudio(waveFileBuffer: Uint8Array, ignoreTruncatedChunks = true, ignoreOverflowingDataChunks = true) {
|
|
15
|
-
|
|
15
|
+
const rawAudio = decodeWaveToFloat32Channels(waveFileBuffer, ignoreTruncatedChunks, ignoreOverflowingDataChunks)
|
|
16
|
+
|
|
17
|
+
return { rawAudio }
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
package/src/cli/CLI.ts
CHANGED
|
@@ -10,10 +10,10 @@ import { SubtitlesConfig, subtitlesToText, timelineToSubtitles } from '../subtit
|
|
|
10
10
|
import { Logger, resetActiveLogger } from '../utilities/Logger.js'
|
|
11
11
|
import { isMainThread, parentPort } from 'node:worker_threads'
|
|
12
12
|
import { encodeFromChannels, getDefaultFFMpegOptionsForSpeech } from '../codecs/FFMpegTranscoder.js'
|
|
13
|
-
import { splitToParagraphs, splitToWords
|
|
13
|
+
import { splitToParagraphs, splitToWords } from '../nlp/Segmentation.js'
|
|
14
14
|
import { playAudioSamplesWithKeyboardControls, playAudioWithWordTimeline } from '../audio/AudioPlayer.js'
|
|
15
15
|
import { extendDeep } from '../utilities/ObjectUtilities.js'
|
|
16
|
-
import { Timeline, TimelineEntry, addTimeOffsetToTimeline,
|
|
16
|
+
import { Timeline, TimelineEntry, addTimeOffsetToTimeline, addWordTextOffsetsToTimelineInPlace, roundTimelineProperties } from '../utilities/Timeline.js'
|
|
17
17
|
import { ensureDir, existsSync, readAndParseJsonFile, readdir, readFileAsUtf8, writeFileSafe } from '../utilities/FileSystem.js'
|
|
18
18
|
import { formatLanguageCodeWithName, getShortLanguageCode } from '../utilities/Locale.js'
|
|
19
19
|
import { APIOptions } from '../api/APIOptions.js'
|
|
@@ -509,7 +509,7 @@ export async function speak(operationData: CLIOperationData) {
|
|
|
509
509
|
const { audio: synthesizedAudio, timeline } = await API.synthesize(textSegments, options, onSegment, undefined)
|
|
510
510
|
|
|
511
511
|
if (plainText) {
|
|
512
|
-
|
|
512
|
+
addWordTextOffsetsToTimelineInPlace(timeline, plainText)
|
|
513
513
|
}
|
|
514
514
|
|
|
515
515
|
if (outputFilenames.length > 0) {
|
|
@@ -1884,7 +1884,7 @@ async function checkOutputFilenames(outputFilenames: string[], acceptMediaOutput
|
|
|
1884
1884
|
async function writeOutputFilesForSegment(outputFilenames: string[], index: number, total: number, audio: RawAudio, timeline: Timeline, text: string, language: string, allowOverwrite: boolean) {
|
|
1885
1885
|
const digitCount = Math.max((total + 1).toString().length, 2)
|
|
1886
1886
|
|
|
1887
|
-
const segmentWords = (await splitToWords(text, language)).
|
|
1887
|
+
const segmentWords = (await splitToWords(text, language)).nonPunctuationWords
|
|
1888
1888
|
|
|
1889
1889
|
const segmentJoinedWords = segmentWords.join(' ').trim()
|
|
1890
1890
|
|
package/src/codecs/TIMITCodec.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { decodeToChannels } from '../audio/AudioBufferConversion.js'
|
|
1
|
+
import { decodeToChannels, SampleFormat } from '../audio/AudioBufferConversion.js'
|
|
2
2
|
import { decodeAscii } from '../encodings/Ascii.js'
|
|
3
3
|
import { readFileAsBinary } from '../utilities/FileSystem.js'
|
|
4
|
-
import { SampleFormat } from './WaveCodec.js'
|
|
5
4
|
|
|
6
5
|
export async function decodeTimitAudioFile(filename: string) {
|
|
7
6
|
return decodeTimitAudio(await readFileAsBinary(filename))
|
package/src/denoising/RNNoise.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { float32ToInt16Pcm } from '../audio/AudioBufferConversion.js'
|
|
2
2
|
import { concatFloat32Arrays } from '../utilities/Utilities.js'
|
|
3
|
-
import {
|
|
3
|
+
import { wrapEmscriptenModuleHeap } from 'wasm-heap-manager'
|
|
4
4
|
import { Logger } from '../utilities/Logger.js'
|
|
5
5
|
import { RawAudio, cloneRawAudio } from '../audio/AudioUtilities.js'
|
|
6
6
|
|
|
@@ -24,15 +24,15 @@ export async function denoiseAudio(rawAudio: RawAudio) {
|
|
|
24
24
|
const m = await getRnnoiseInstance()
|
|
25
25
|
|
|
26
26
|
logger.start('Process with RNNoise')
|
|
27
|
-
const
|
|
27
|
+
const wasmHeap = wrapEmscriptenModuleHeap(m)
|
|
28
28
|
|
|
29
29
|
const stateSize = m._rnnoise_get_size()
|
|
30
30
|
const frameSize = m._rnnoise_get_frame_size()
|
|
31
31
|
|
|
32
32
|
const denoiseState = m._rnnoise_create(0)
|
|
33
33
|
|
|
34
|
-
const inputRef =
|
|
35
|
-
const outputRef =
|
|
34
|
+
const inputRef = wasmHeap.allocFloat32Array(frameSize)
|
|
35
|
+
const outputRef = wasmHeap.allocFloat32Array(frameSize)
|
|
36
36
|
|
|
37
37
|
const floatSamples = rawAudio.audioChannels[0]
|
|
38
38
|
const int16Samples = float32ToInt16Pcm(floatSamples)
|
|
@@ -70,7 +70,7 @@ export async function denoiseAudio(rawAudio: RawAudio) {
|
|
|
70
70
|
outputNewFrame(outputRef.view.slice(), lastFrameVadProbability)
|
|
71
71
|
|
|
72
72
|
m._rnnoise_destroy(denoiseState)
|
|
73
|
-
|
|
73
|
+
wasmHeap.freeAll()
|
|
74
74
|
|
|
75
75
|
const int16DenoisedSamplesAsFloats = concatFloat32Arrays(processedFrames)
|
|
76
76
|
|
package/src/dsp/FFT.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ComplexNumber } from '../math/VectorMath.js'
|
|
2
2
|
import { concatFloat32Arrays, isWasmSimdSupported } from '../utilities/Utilities.js'
|
|
3
|
-
import {
|
|
3
|
+
import { createWasmHeapManager } from 'wasm-heap-manager'
|
|
4
4
|
|
|
5
5
|
// Compute short-term Fourier transform (real-valued)
|
|
6
6
|
export async function stftr(samples: Float32Array, fftOrder: number, windowSize: number, hopSize: number, windowType: WindowType) {
|
|
@@ -15,7 +15,7 @@ export async function stftr(samples: Float32Array, fftOrder: number, windowSize:
|
|
|
15
15
|
|
|
16
16
|
// Incrementally generate short-term Fourier transform frames (real-valued)
|
|
17
17
|
export async function* stftrGenerator(samples: Float32Array, fftOrder: number, windowSize: number, hopSize: number, windowType: WindowType) {
|
|
18
|
-
if (fftOrder % 2
|
|
18
|
+
if (fftOrder % 2 !== 0 || windowSize % 2 !== 0) {
|
|
19
19
|
throw new Error('FFT order and window size must be multiples of 2')
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -35,17 +35,19 @@ export async function* stftrGenerator(samples: Float32Array, fftOrder: number, w
|
|
|
35
35
|
const windowWeights = getWindowWeights(windowType, windowSize)
|
|
36
36
|
|
|
37
37
|
const m = await getPFFFTInstance(await isPffftSimdSupportedForFFTOrder(fftOrder))
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
|
|
39
|
+
const wasmHeap = createWasmHeapManager(
|
|
40
|
+
() => m.HEAPU8.buffer,
|
|
41
|
+
m._pffft_aligned_malloc,
|
|
42
|
+
m._pffft_aligned_free,
|
|
43
|
+
)
|
|
42
44
|
|
|
43
45
|
const statePtr = m._pffft_new_setup(fftOrder, 0)
|
|
44
46
|
|
|
45
47
|
const sampleCount = samples.length
|
|
46
|
-
const frameBufferRef =
|
|
47
|
-
const binsBufferRef =
|
|
48
|
-
const workBufferRef =
|
|
48
|
+
const frameBufferRef = wasmHeap.allocFloat32Array(fftOrder)
|
|
49
|
+
const binsBufferRef = wasmHeap.allocFloat32Array(fftOrder * 2)
|
|
50
|
+
const workBufferRef = wasmHeap.allocFloat32Array(fftOrder * 2)
|
|
49
51
|
|
|
50
52
|
for (let offset = 0; offset < sampleCount; offset += hopSize) {
|
|
51
53
|
const windowSamples = samples.subarray(offset, offset + windowSize)
|
|
@@ -68,12 +70,12 @@ export async function* stftrGenerator(samples: Float32Array, fftOrder: number, w
|
|
|
68
70
|
|
|
69
71
|
m._pffft_destroy_setup(statePtr)
|
|
70
72
|
|
|
71
|
-
|
|
73
|
+
wasmHeap.freeAll()
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
// Compute short-term inverse Fourier transform (real-valued)
|
|
75
77
|
export async function stiftr(binsForFrames: Float32Array[], fftOrder: number, windowSize: number, hopSize: number, windowType: WindowType, expectedOutputLength?: number) {
|
|
76
|
-
if (fftOrder % 2
|
|
78
|
+
if (fftOrder % 2 !== 0 || windowSize % 2 !== 0) {
|
|
77
79
|
throw new Error('FFT order and window size must multiples of 2')
|
|
78
80
|
}
|
|
79
81
|
|
|
@@ -101,16 +103,17 @@ export async function stiftr(binsForFrames: Float32Array[], fftOrder: number, wi
|
|
|
101
103
|
|
|
102
104
|
const m = await getPFFFTInstance(await isPffftSimdSupportedForFFTOrder(fftOrder))
|
|
103
105
|
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
const wasmHeap = createWasmHeapManager(
|
|
107
|
+
() => m.HEAPU8.buffer,
|
|
108
|
+
m._pffft_aligned_malloc,
|
|
109
|
+
m._pffft_aligned_free,
|
|
110
|
+
)
|
|
108
111
|
|
|
109
112
|
const statePtr = m._pffft_new_setup(fftOrder, 0)
|
|
110
113
|
|
|
111
|
-
const frameBufferRef =
|
|
112
|
-
const binsRef =
|
|
113
|
-
const workBufferRef =
|
|
114
|
+
const frameBufferRef = wasmHeap.allocFloat32Array(fftOrder)
|
|
115
|
+
const binsRef = wasmHeap.allocFloat32Array(fftOrder * 2)
|
|
116
|
+
const workBufferRef = wasmHeap.allocFloat32Array(fftOrder * 2)
|
|
114
117
|
|
|
115
118
|
const sumOfSquaredWeightsForSample = new Float32Array(outSampleCount)
|
|
116
119
|
|
|
@@ -140,7 +143,7 @@ export async function stiftr(binsForFrames: Float32Array[], fftOrder: number, wi
|
|
|
140
143
|
}
|
|
141
144
|
|
|
142
145
|
m._pffft_destroy_setup(statePtr)
|
|
143
|
-
|
|
146
|
+
wasmHeap.freeAll()
|
|
144
147
|
|
|
145
148
|
// Divide each output sample by the sum of squared weights
|
|
146
149
|
for (let i = 0; i < outSamples.length; i++) {
|
package/src/dsp/Rubberband.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RawAudio } from '../audio/AudioUtilities.js'
|
|
2
2
|
import { extendDeep } from '../utilities/ObjectUtilities.js'
|
|
3
3
|
import { concatFloat32Arrays } from '../utilities/Utilities.js'
|
|
4
|
-
import { Float32ArrayRef,
|
|
4
|
+
import { Float32ArrayRef, wrapEmscriptenModuleHeap } from 'wasm-heap-manager'
|
|
5
5
|
|
|
6
6
|
let rubberbandInstance: any
|
|
7
7
|
|
|
@@ -14,7 +14,7 @@ export async function stretchTimePitch(rawAudio: RawAudio, speed: number, pitchS
|
|
|
14
14
|
const sampleRate = rawAudio.sampleRate
|
|
15
15
|
|
|
16
16
|
const m = await getRubberbandInstance()
|
|
17
|
-
const
|
|
17
|
+
const wasmHeap = wrapEmscriptenModuleHeap(m)
|
|
18
18
|
|
|
19
19
|
const optionFlags = rubberBandOptionsToFlags(options)
|
|
20
20
|
|
|
@@ -25,11 +25,11 @@ export async function stretchTimePitch(rawAudio: RawAudio, speed: number, pitchS
|
|
|
25
25
|
const samplesRequired = m._rubberband_get_samples_required(statePtr)
|
|
26
26
|
const bufferSize = Math.min(samplesRequired, sampleCount)
|
|
27
27
|
|
|
28
|
-
const bufferChannelPtrsRef =
|
|
28
|
+
const bufferChannelPtrsRef = wasmHeap.allocUint32Array(bufferSize)
|
|
29
29
|
const bufferChannelRefs: Float32ArrayRef[] = []
|
|
30
30
|
|
|
31
31
|
for (let i = 0; i < channelCount; i++) {
|
|
32
|
-
const bufferChannelRef =
|
|
32
|
+
const bufferChannelRef = wasmHeap.allocFloat32Array(bufferSize)
|
|
33
33
|
bufferChannelPtrsRef.view[i] = bufferChannelRef.address
|
|
34
34
|
|
|
35
35
|
bufferChannelRefs.push(bufferChannelRef)
|
|
@@ -102,7 +102,7 @@ export async function stretchTimePitch(rawAudio: RawAudio, speed: number, pitchS
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
m._rubberband_delete(statePtr)
|
|
105
|
-
|
|
105
|
+
wasmHeap.freeAll()
|
|
106
106
|
|
|
107
107
|
const outputAudioChannels = outputAudioChannelChunks.map(chunks => concatFloat32Arrays(chunks))
|
|
108
108
|
const outputRawAudio: RawAudio = { audioChannels: outputAudioChannels, sampleRate }
|
package/src/dsp/Sonic.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RawAudio } from '../audio/AudioUtilities.js'
|
|
2
|
-
import {
|
|
2
|
+
import { wrapEmscriptenModuleHeap } from 'wasm-heap-manager'
|
|
3
3
|
|
|
4
4
|
let sonicInstance: any
|
|
5
5
|
|
|
@@ -10,13 +10,13 @@ export async function stretchTimePitch(rawAudio: RawAudio, speed: number, pitchS
|
|
|
10
10
|
const inputSampleCount = rawAudio.audioChannels[0].length
|
|
11
11
|
|
|
12
12
|
const m = await getSonicInstance()
|
|
13
|
-
const
|
|
13
|
+
const wasmHeap = wrapEmscriptenModuleHeap(m)
|
|
14
14
|
|
|
15
15
|
const streamPtr = m._sonicCreateStream(sampleRate, channelCount)
|
|
16
16
|
m._sonicSetSpeed(streamPtr, speed)
|
|
17
17
|
m._sonicSetPitch(streamPtr, pitchScale)
|
|
18
18
|
|
|
19
|
-
const inputSamplesRef =
|
|
19
|
+
const inputSamplesRef = wasmHeap.allocFloat32Array(inputSampleCount)
|
|
20
20
|
inputSamplesRef.view.set(inputSamples)
|
|
21
21
|
|
|
22
22
|
const writeSuccess = m._sonicWriteFloatToStream(streamPtr, inputSamplesRef.address, inputSampleCount)
|
|
@@ -33,9 +33,9 @@ export async function stretchTimePitch(rawAudio: RawAudio, speed: number, pitchS
|
|
|
33
33
|
|
|
34
34
|
const samplesAvailable = m._sonicSamplesAvailable(streamPtr)
|
|
35
35
|
|
|
36
|
-
const outputSamplesRef =
|
|
36
|
+
const outputSamplesRef = wasmHeap.allocFloat32Array(samplesAvailable)
|
|
37
37
|
|
|
38
|
-
const samplesRead = m._sonicReadFloatFromStream(streamPtr, outputSamplesRef.address, outputSamplesRef.
|
|
38
|
+
const samplesRead = m._sonicReadFloatFromStream(streamPtr, outputSamplesRef.address, outputSamplesRef.allocatedByteCount)
|
|
39
39
|
|
|
40
40
|
const outputSamples = outputSamplesRef.view.slice(0, samplesRead)
|
|
41
41
|
|
|
@@ -43,7 +43,7 @@ export async function stretchTimePitch(rawAudio: RawAudio, speed: number, pitchS
|
|
|
43
43
|
|
|
44
44
|
m._sonicDestroyStream(streamPtr)
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
wasmHeap.freeAll()
|
|
47
47
|
|
|
48
48
|
return resultAudio
|
|
49
49
|
}
|