echogarden 1.8.6 → 2.0.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 (299) hide show
  1. package/README.md +23 -18
  2. package/data/schemas/options.json +52 -47
  3. package/dist/alignment/DTWSequenceAlignment.d.ts +1 -1
  4. package/dist/alignment/DTWSequenceAlignment.js +5 -5
  5. package/dist/alignment/DTWSequenceAlignment.js.map +1 -1
  6. package/dist/alignment/SpeechAlignment.js +6 -6
  7. package/dist/alignment/SpeechAlignment.js.map +1 -1
  8. package/dist/api/Alignment.d.ts +1 -1
  9. package/dist/api/Alignment.js +49 -11
  10. package/dist/api/Alignment.js.map +1 -1
  11. package/dist/api/Denoising.d.ts +4 -2
  12. package/dist/api/Denoising.js +65 -13
  13. package/dist/api/Denoising.js.map +1 -1
  14. package/dist/api/SourceSeparation.js +9 -6
  15. package/dist/api/SourceSeparation.js.map +1 -1
  16. package/dist/api/SpeechLanguageDetection.js +4 -4
  17. package/dist/api/SpeechLanguageDetection.js.map +1 -1
  18. package/dist/api/Synthesis.d.ts +2 -2
  19. package/dist/api/Synthesis.js +10 -7
  20. package/dist/api/Synthesis.js.map +1 -1
  21. package/dist/api/VoiceActivityDetection.js +2 -2
  22. package/dist/api/VoiceActivityDetection.js.map +1 -1
  23. package/dist/audio/AudioBufferConversion.d.ts +2 -2
  24. package/dist/audio/AudioBufferConversion.js +77 -43
  25. package/dist/audio/AudioBufferConversion.js.map +1 -1
  26. package/dist/audio/AudioPlayer.d.ts +7 -5
  27. package/dist/audio/AudioPlayer.js +136 -50
  28. package/dist/audio/AudioPlayer.js.map +1 -1
  29. package/dist/audio/AudioUtilities.d.ts +4 -3
  30. package/dist/audio/AudioUtilities.js +30 -15
  31. package/dist/audio/AudioUtilities.js.map +1 -1
  32. package/dist/audio/SoxPath.js +3 -3
  33. package/dist/audio/SoxPath.js.map +1 -1
  34. package/dist/build-tools/MakeTarballsForInstalledPackages.js +3 -3
  35. package/dist/build-tools/MakeTarballsForInstalledPackages.js.map +1 -1
  36. package/dist/cli/CLI.js +30 -36
  37. package/dist/cli/CLI.js.map +1 -1
  38. package/dist/cli/CLIConfigFile.js +2 -2
  39. package/dist/cli/CLIConfigFile.js.map +1 -1
  40. package/dist/cli/CLIOptions.d.ts +2 -0
  41. package/dist/cli/CLIOptions.js +1 -1
  42. package/dist/cli/CLIOptions.js.map +1 -1
  43. package/dist/codecs/FFMpegTranscoder.d.ts +3 -3
  44. package/dist/codecs/FFMpegTranscoder.js +6 -6
  45. package/dist/codecs/FFMpegTranscoder.js.map +1 -1
  46. package/dist/codecs/TIMITCodec.d.ts +1 -1
  47. package/dist/codecs/TIMITCodec.js +4 -3
  48. package/dist/codecs/TIMITCodec.js.map +1 -1
  49. package/dist/codecs/WaveCodec.d.ts +3 -3
  50. package/dist/codecs/WaveCodec.js +37 -34
  51. package/dist/codecs/WaveCodec.js.map +1 -1
  52. package/dist/data-structures/DynamicTypedArray.d.ts +17 -0
  53. package/dist/data-structures/DynamicTypedArray.js +47 -0
  54. package/dist/data-structures/DynamicTypedArray.js.map +1 -0
  55. package/dist/data-structures/Queue.js.map +1 -0
  56. package/dist/data-structures/WindowedList.js.map +1 -0
  57. package/dist/denoising/NSNet2.d.ts +26 -0
  58. package/dist/denoising/NSNet2.js +128 -0
  59. package/dist/denoising/NSNet2.js.map +1 -0
  60. package/dist/denoising/RNNoise.js +3 -3
  61. package/dist/denoising/RNNoise.js.map +1 -1
  62. package/dist/dsp/FFT.d.ts +6 -1
  63. package/dist/dsp/FFT.js +17 -0
  64. package/dist/dsp/FFT.js.map +1 -1
  65. package/dist/encodings/Ascii.d.ts +10 -0
  66. package/dist/encodings/Ascii.js +41 -0
  67. package/dist/encodings/Ascii.js.map +1 -0
  68. package/dist/encodings/Base64.d.ts +5 -0
  69. package/dist/encodings/Base64.js +114 -0
  70. package/dist/encodings/Base64.js.map +1 -0
  71. package/dist/encodings/Hex.d.ts +3 -0
  72. package/dist/encodings/Hex.js +52 -0
  73. package/dist/encodings/Hex.js.map +1 -0
  74. package/dist/{utilities → encodings}/LEB128.d.ts +1 -1
  75. package/dist/{utilities → encodings}/LEB128.js +4 -4
  76. package/dist/encodings/LEB128.js.map +1 -0
  77. package/dist/{utilities → encodings}/LPVarInt.d.ts +1 -1
  78. package/dist/{utilities → encodings}/LPVarInt.js +6 -6
  79. package/dist/encodings/LPVarInt.js.map +1 -0
  80. package/dist/encodings/TextEncodingsCommon.d.ts +4 -0
  81. package/dist/encodings/TextEncodingsCommon.js +2 -0
  82. package/dist/encodings/TextEncodingsCommon.js.map +1 -0
  83. package/dist/encodings/Utf16.d.ts +10 -0
  84. package/dist/encodings/Utf16.js +36 -0
  85. package/dist/encodings/Utf16.js.map +1 -0
  86. package/dist/encodings/Utf32.d.ts +9 -0
  87. package/dist/encodings/Utf32.js +44 -0
  88. package/dist/encodings/Utf32.js.map +1 -0
  89. package/dist/encodings/Utf8.d.ts +10 -0
  90. package/dist/encodings/Utf8.js +97 -0
  91. package/dist/encodings/Utf8.js.map +1 -0
  92. package/dist/math/MedianFilter.js.map +1 -1
  93. package/dist/nlp/JapaneseSegmentation.js +2 -2
  94. package/dist/nlp/JapaneseSegmentation.js.map +1 -1
  95. package/dist/recognition/GoogleCloudSTT.js +2 -1
  96. package/dist/recognition/GoogleCloudSTT.js.map +1 -1
  97. package/dist/recognition/SileroSTT.js +3 -3
  98. package/dist/recognition/SileroSTT.js.map +1 -1
  99. package/dist/recognition/VoskSTT.js +3 -3
  100. package/dist/recognition/VoskSTT.js.map +1 -1
  101. package/dist/recognition/WhisperCppSTT.js +4 -5
  102. package/dist/recognition/WhisperCppSTT.js.map +1 -1
  103. package/dist/recognition/WhisperSTT.js +8 -7
  104. package/dist/recognition/WhisperSTT.js.map +1 -1
  105. package/dist/server/Client.d.ts +1 -1
  106. package/dist/server/Client.js +2 -1
  107. package/dist/server/Client.js.map +1 -1
  108. package/dist/server/Server.js +5 -4
  109. package/dist/server/Server.js.map +1 -1
  110. package/dist/server/Worker.js +1 -1
  111. package/dist/server/Worker.js.map +1 -1
  112. package/dist/source-separation/MDXNetSourceSeparation.js +1 -1
  113. package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
  114. package/dist/speech-search/DTWSpeechSearch.d.ts +1 -1
  115. package/dist/speech-search/DTWSpeechSearch.js +25 -6
  116. package/dist/speech-search/DTWSpeechSearch.js.map +1 -1
  117. package/dist/subtitles/Subtitles.js +2 -2
  118. package/dist/subtitles/Subtitles.js.map +1 -1
  119. package/dist/synthesis/AzureCognitiveServicesTTS.js +17 -18
  120. package/dist/synthesis/AzureCognitiveServicesTTS.js.map +1 -1
  121. package/dist/synthesis/CoquiServerTTS.js +1 -1
  122. package/dist/synthesis/CoquiServerTTS.js.map +1 -1
  123. package/dist/synthesis/ElevenlabsTTS.js +1 -1
  124. package/dist/synthesis/ElevenlabsTTS.js.map +1 -1
  125. package/dist/synthesis/EspeakTTS.d.ts +0 -1
  126. package/dist/synthesis/EspeakTTS.js +0 -9
  127. package/dist/synthesis/EspeakTTS.js.map +1 -1
  128. package/dist/synthesis/FliteTTS.js +7 -8
  129. package/dist/synthesis/FliteTTS.js.map +1 -1
  130. package/dist/synthesis/GoogleCloudTTS.d.ts +1 -1
  131. package/dist/synthesis/GoogleCloudTTS.js +2 -1
  132. package/dist/synthesis/GoogleCloudTTS.js.map +1 -1
  133. package/dist/synthesis/GoogleTranslateTTS.d.ts +1 -1
  134. package/dist/synthesis/GoogleTranslateTTS.js +4 -3
  135. package/dist/synthesis/GoogleTranslateTTS.js.map +1 -1
  136. package/dist/synthesis/MicrosoftEdgeTTS.js +32 -7
  137. package/dist/synthesis/MicrosoftEdgeTTS.js.map +1 -1
  138. package/dist/synthesis/SapiTTS.js +1 -1
  139. package/dist/synthesis/SapiTTS.js.map +1 -1
  140. package/dist/synthesis/StreamlabsPollyTTS.d.ts +1 -1
  141. package/dist/synthesis/StreamlabsPollyTTS.js +1 -1
  142. package/dist/synthesis/StreamlabsPollyTTS.js.map +1 -1
  143. package/dist/synthesis/SvoxPicoTTS.js +6 -5
  144. package/dist/synthesis/SvoxPicoTTS.js.map +1 -1
  145. package/dist/synthesis/VitsTTS.js +2 -2
  146. package/dist/synthesis/VitsTTS.js.map +1 -1
  147. package/dist/tests/Test.js +0 -25
  148. package/dist/tests/Test.js.map +1 -1
  149. package/dist/text-language-detection/FastTextLanguageDetection.js +2 -2
  150. package/dist/text-language-detection/FastTextLanguageDetection.js.map +1 -1
  151. package/dist/typings/TypedArray.d.ts +4 -0
  152. package/dist/typings/TypedArray.js +2 -0
  153. package/dist/typings/TypedArray.js.map +1 -0
  154. package/dist/utilities/BinaryArrayConversion.d.ts +12 -22
  155. package/dist/utilities/BinaryArrayConversion.js +40 -90
  156. package/dist/utilities/BinaryArrayConversion.js.map +1 -1
  157. package/dist/utilities/BinaryUtilities.d.ts +13 -0
  158. package/dist/utilities/BinaryUtilities.js +113 -0
  159. package/dist/utilities/BinaryUtilities.js.map +1 -0
  160. package/dist/utilities/Compression.js +7 -6
  161. package/dist/utilities/Compression.js.map +1 -1
  162. package/dist/utilities/FileDownloader.js +26 -47
  163. package/dist/utilities/FileDownloader.js.map +1 -1
  164. package/dist/utilities/FileReader.d.ts +14 -0
  165. package/dist/utilities/FileReader.js +71 -0
  166. package/dist/utilities/FileReader.js.map +1 -0
  167. package/dist/utilities/FileSystem.d.ts +19 -17
  168. package/dist/utilities/FileSystem.js +170 -106
  169. package/dist/utilities/FileSystem.js.map +1 -1
  170. package/dist/utilities/FileWriter.d.ts +12 -0
  171. package/dist/utilities/FileWriter.js +60 -0
  172. package/dist/utilities/FileWriter.js.map +1 -0
  173. package/dist/utilities/NpmUtilities.d.ts +1 -0
  174. package/dist/utilities/NpmUtilities.js +14 -0
  175. package/dist/utilities/NpmUtilities.js.map +1 -0
  176. package/dist/utilities/PackageManager.js +7 -5
  177. package/dist/utilities/PackageManager.js.map +1 -1
  178. package/dist/utilities/PathUtilities.d.ts +8 -0
  179. package/dist/utilities/PathUtilities.js +28 -7
  180. package/dist/utilities/PathUtilities.js.map +1 -1
  181. package/dist/utilities/SignalChannel.d.ts +3 -1
  182. package/dist/utilities/SignalChannel.js +9 -5
  183. package/dist/utilities/SignalChannel.js.map +1 -1
  184. package/dist/utilities/StringBuilder.d.ts +10 -0
  185. package/dist/utilities/StringBuilder.js +39 -0
  186. package/dist/utilities/StringBuilder.js.map +1 -0
  187. package/dist/utilities/StringUtilities.js.map +1 -1
  188. package/dist/utilities/TarballMaker.js +6 -7
  189. package/dist/utilities/TarballMaker.js.map +1 -1
  190. package/dist/utilities/Timer.js +2 -2
  191. package/dist/utilities/Timer.js.map +1 -1
  192. package/dist/utilities/Utilities.d.ts +11 -4
  193. package/dist/utilities/Utilities.js +70 -18
  194. package/dist/utilities/Utilities.js.map +1 -1
  195. package/dist/utilities/VirtualFileReadStream.d.ts +3 -3
  196. package/dist/utilities/VirtualFileReadStream.js.map +1 -1
  197. package/dist/utilities/WasmMemoryManager.d.ts +5 -2
  198. package/dist/utilities/WasmMemoryManager.js +15 -3
  199. package/dist/utilities/WasmMemoryManager.js.map +1 -1
  200. package/docs/API.md +1 -1
  201. package/docs/Licenses.md +3 -0
  202. package/docs/Options.md +9 -3
  203. package/docs/Tasklist.md +0 -1
  204. package/docs/Technical.md +2 -2
  205. package/package.json +20 -22
  206. package/src/alignment/DTWSequenceAlignment.ts +5 -5
  207. package/src/alignment/SpeechAlignment.ts +6 -6
  208. package/src/api/Alignment.ts +65 -12
  209. package/src/api/Denoising.ts +97 -17
  210. package/src/api/SourceSeparation.ts +11 -8
  211. package/src/api/SpeechLanguageDetection.ts +4 -4
  212. package/src/api/Synthesis.ts +15 -11
  213. package/src/api/VoiceActivityDetection.ts +2 -2
  214. package/src/audio/AudioBufferConversion.ts +84 -45
  215. package/src/audio/AudioPlayer.ts +156 -54
  216. package/src/audio/AudioUtilities.ts +38 -22
  217. package/src/audio/SoxPath.ts +3 -3
  218. package/src/build-tools/MakeTarballsForInstalledPackages.ts +3 -3
  219. package/src/cli/CLI.ts +30 -36
  220. package/src/cli/CLIConfigFile.ts +2 -2
  221. package/src/cli/CLIOptions.ts +4 -1
  222. package/src/codecs/FFMpegTranscoder.ts +11 -11
  223. package/src/codecs/TIMITCodec.ts +5 -4
  224. package/src/codecs/WaveCodec.ts +42 -39
  225. package/src/data-structures/DynamicTypedArray.ts +64 -0
  226. package/src/denoising/NSNet2.ts +182 -0
  227. package/src/denoising/RNNoise.ts +3 -3
  228. package/src/dsp/FFT.ts +21 -1
  229. package/src/encodings/Ascii.ts +60 -0
  230. package/src/encodings/Base64.ts +157 -0
  231. package/src/encodings/Hex.ts +67 -0
  232. package/src/{utilities → encodings}/LEB128.ts +4 -4
  233. package/src/{utilities → encodings}/LPVarInt.ts +6 -6
  234. package/src/encodings/TextEncodingsCommon.ts +4 -0
  235. package/src/encodings/Utf16.ts +52 -0
  236. package/src/encodings/Utf32.ts +62 -0
  237. package/src/encodings/Utf8.ts +117 -0
  238. package/src/math/MedianFilter.ts +0 -2
  239. package/src/nlp/JapaneseSegmentation.ts +2 -2
  240. package/src/recognition/GoogleCloudSTT.ts +2 -1
  241. package/src/recognition/SileroSTT.ts +3 -3
  242. package/src/recognition/VoskSTT.ts +3 -3
  243. package/src/recognition/WhisperCppSTT.ts +4 -5
  244. package/src/recognition/WhisperSTT.ts +8 -7
  245. package/src/server/Client.ts +4 -3
  246. package/src/server/Server.ts +6 -5
  247. package/src/server/Worker.ts +1 -1
  248. package/src/source-separation/MDXNetSourceSeparation.ts +1 -1
  249. package/src/speech-search/DTWSpeechSearch.ts +36 -7
  250. package/src/subtitles/Subtitles.ts +3 -3
  251. package/src/synthesis/AzureCognitiveServicesTTS.ts +18 -15
  252. package/src/synthesis/CoquiServerTTS.ts +2 -2
  253. package/src/synthesis/ElevenlabsTTS.ts +1 -1
  254. package/src/synthesis/EspeakTTS.ts +0 -15
  255. package/src/synthesis/FliteTTS.ts +7 -8
  256. package/src/synthesis/GoogleCloudTTS.ts +3 -2
  257. package/src/synthesis/GoogleTranslateTTS.ts +5 -4
  258. package/src/synthesis/MicrosoftEdgeTTS.ts +47 -15
  259. package/src/synthesis/SapiTTS.ts +1 -1
  260. package/src/synthesis/StreamlabsPollyTTS.ts +1 -1
  261. package/src/synthesis/SvoxPicoTTS.ts +8 -7
  262. package/src/synthesis/VitsTTS.ts +2 -2
  263. package/src/tests/Test.ts +0 -30
  264. package/src/text-language-detection/FastTextLanguageDetection.ts +2 -2
  265. package/src/typings/Fillers.d.ts +2 -4
  266. package/src/typings/TypedArray.ts +5 -0
  267. package/src/utilities/BinaryArrayConversion.ts +51 -125
  268. package/src/utilities/BinaryUtilities.ts +138 -0
  269. package/src/utilities/Compression.ts +7 -6
  270. package/src/utilities/FileDownloader.ts +27 -52
  271. package/src/utilities/FileReader.ts +89 -0
  272. package/src/utilities/FileSystem.ts +215 -116
  273. package/src/utilities/FileWriter.ts +72 -0
  274. package/src/utilities/NpmUtilities.ts +23 -0
  275. package/src/utilities/PackageManager.ts +8 -5
  276. package/src/utilities/PathUtilities.ts +36 -7
  277. package/src/utilities/SignalChannel.ts +14 -7
  278. package/src/utilities/StringBuilder.ts +45 -0
  279. package/src/utilities/StringUtilities.ts +2 -0
  280. package/src/utilities/TarballMaker.ts +6 -7
  281. package/src/utilities/Timer.ts +4 -2
  282. package/src/utilities/Utilities.ts +80 -23
  283. package/src/utilities/VirtualFileReadStream.ts +5 -5
  284. package/src/utilities/WasmMemoryManager.ts +21 -5
  285. package/tsconfig.json +96 -0
  286. package/dist/utilities/DynamicUint8Array.d.ts +0 -9
  287. package/dist/utilities/DynamicUint8Array.js +0 -31
  288. package/dist/utilities/DynamicUint8Array.js.map +0 -1
  289. package/dist/utilities/LEB128.js.map +0 -1
  290. package/dist/utilities/LPVarInt.js.map +0 -1
  291. package/dist/utilities/Queue.js.map +0 -1
  292. package/dist/utilities/WindowedList.js.map +0 -1
  293. package/src/utilities/DynamicUint8Array.ts +0 -39
  294. /package/dist/{utilities → data-structures}/Queue.d.ts +0 -0
  295. /package/dist/{utilities → data-structures}/Queue.js +0 -0
  296. /package/dist/{utilities → data-structures}/WindowedList.d.ts +0 -0
  297. /package/dist/{utilities → data-structures}/WindowedList.js +0 -0
  298. /package/src/{utilities → data-structures}/Queue.ts +0 -0
  299. /package/src/{utilities → data-structures}/WindowedList.ts +0 -0
@@ -92,10 +92,12 @@ export async function align(input: AudioSourceParam, transcript: string, options
92
92
  let granularities: DtwGranularity[]
93
93
  let windowDurations: number[]
94
94
 
95
- if (typeof options.dtw!.granularity == 'string') {
96
- granularities = [options.dtw!.granularity]
97
- } else if (Array.isArray(options.dtw!.granularity)) {
98
- granularities = options.dtw!.granularity
95
+ const dtwOptions = options.dtw!
96
+
97
+ if (typeof dtwOptions.granularity == 'string') {
98
+ granularities = [dtwOptions.granularity]
99
+ } else if (Array.isArray(dtwOptions.granularity)) {
100
+ granularities = dtwOptions.granularity
99
101
  } else {
100
102
  if (sourceAudioDuration < 1 * 60) {
101
103
  // If up to 1 minute, set granularity to high, single pass
@@ -112,17 +114,68 @@ export async function align(input: AudioSourceParam, transcript: string, options
112
114
  }
113
115
  }
114
116
 
115
- if (options.dtw!.windowDuration) {
116
- if (typeof options.dtw!.windowDuration === 'number') {
117
- windowDurations = [options.dtw!.windowDuration]
118
- } else if (Array.isArray(options.dtw!.windowDuration)) {
119
- windowDurations = options.dtw!.windowDuration
117
+ if (dtwOptions.windowDuration) {
118
+ function tryParsePercentageWindowDuration(durationString: string) {
119
+ durationString = durationString.trim()
120
+
121
+ const parseResult = durationString.match(/^([0-9]+)%$/)
122
+
123
+ if (parseResult == null) {
124
+ throw new Error(`A DTW window duration, when provided as a string, must be formatted as an integer percentage value like '15%'.`)
125
+ }
126
+
127
+ const percentageValue = parseInt(parseResult[1])
128
+
129
+ if (percentageValue == null || isNaN(percentageValue) || percentageValue <= 0 || percentageValue > 100) {
130
+ throw new Error(`'A DTW window duration, when provided as a percentage value, must be between 0 (non-inclusive) and 100 (inclusive).`)
131
+ }
132
+
133
+ let durationSeconds = percentageValue / 100 * sourceAudioDuration
134
+ durationSeconds = Math.ceil(durationSeconds)
135
+ durationSeconds = Math.min(durationSeconds, sourceAudioDuration)
136
+
137
+ return durationSeconds
138
+ }
139
+
140
+ if (typeof dtwOptions.windowDuration === 'number') {
141
+ const duration = Math.min(dtwOptions.windowDuration, sourceAudioDuration)
142
+
143
+ windowDurations = [duration]
144
+ } else if (typeof dtwOptions.windowDuration === 'string') {
145
+ const durationString = dtwOptions.windowDuration.trim()
146
+
147
+ const durationSeconds = tryParsePercentageWindowDuration(durationString)
148
+
149
+ windowDurations = [durationSeconds]
150
+ } else if (Array.isArray(dtwOptions.windowDuration)) {
151
+ const durationsValues = dtwOptions.windowDuration
152
+
153
+ if (durationsValues.length < 1) {
154
+ throw new Error(`DTW window durations, when given as an array, must have at least one element.`)
155
+ }
156
+
157
+ const durations: number[] = []
158
+
159
+ for (const durationValue of durationsValues) {
160
+ let durationSeconds: number
161
+
162
+ if (typeof durationValue === 'number') {
163
+ durationSeconds = durationValue
164
+ durationSeconds = Math.min(durationSeconds, sourceAudioDuration)
165
+ } else {
166
+ durationSeconds = tryParsePercentageWindowDuration(durationValue)
167
+ }
168
+
169
+ durations.push(durationSeconds)
170
+ }
171
+
172
+ windowDurations = durations
120
173
  } else {
121
- throw new Error(`'dtw.windowDuration' must be a number or an array of numbers.`)
174
+ throw new Error(`'dtw.windowDuration' must be a number or a percentage string, or array of numbers / percentage strings.`)
122
175
  }
123
176
  } else {
124
177
  if (granularities.length > 2) {
125
- throw new Error(`More than two passes requested, this requires window durations to be explicitly specified for each pass. For example 'dtw.windowDuration=[600,60,10]'.`)
178
+ throw new Error(`More than two passes requested, this requires window durations to be explicitly specified for each pass. For example 'dtw.windowDuration=['20%',60,10]'.`)
126
179
  }
127
180
 
128
181
  if (sourceAudioDuration < 5 * 60) {
@@ -361,7 +414,7 @@ export interface AlignmentOptions {
361
414
 
362
415
  dtw?: {
363
416
  granularity?: DtwGranularity | DtwGranularity[]
364
- windowDuration?: number | number[]
417
+ windowDuration?: number | string | (string | number)[]
365
418
  phoneAlignmentMethod?: PhoneAlignmentMethod
366
419
  }
367
420
 
@@ -1,12 +1,14 @@
1
1
  import { extendDeep } from '../utilities/ObjectUtilities.js'
2
2
 
3
- import { AudioSourceParam, RawAudio, applyGainDecibelsInPlace, ensureRawAudio, getSamplePeakDecibels, mixAudio, normalizeAudioLevelInPlace } from '../audio/AudioUtilities.js'
3
+ import { AudioSourceParam, RawAudio, applyGainDecibels, applyGainDecibelsInPlace, attenuateIfClippingInPlace, ensureRawAudio, getSamplePeakDecibels, mixAudio, normalizeAudioLevelInPlace } from '../audio/AudioUtilities.js'
4
4
  import { Logger } from '../utilities/Logger.js'
5
5
 
6
6
  import { logToStderr } from '../utilities/Utilities.js'
7
7
  import { resampleAudioSpeex } from '../dsp/SpeexResampler.js'
8
8
  import { EngineMetadata } from './Common.js'
9
9
  import chalk from 'chalk'
10
+ import { defaultNSNet2Options, NSNet2Options } from '../denoising/NSNet2.js'
11
+ import { loadPackage } from '../utilities/PackageManager.js'
10
12
 
11
13
  const log = logToStderr
12
14
 
@@ -18,26 +20,34 @@ export async function denoise(input: AudioSourceParam, options: DenoisingOptions
18
20
 
19
21
  const inputRawAudio = await ensureRawAudio(input)
20
22
 
21
- const processingSampleRate = 48000
22
-
23
- logger.start(`Resample audio to ${processingSampleRate} Hz`)
24
- const resampledRawAudio = await resampleAudioSpeex(inputRawAudio, processingSampleRate, 3)
25
-
26
- logger.start(`Initialize ${options.method} module`)
23
+ logger.start(`Initialize ${options.engine} module`)
27
24
 
28
25
  let denoisedAudio: RawAudio
29
26
 
30
- switch (options.method) {
27
+ switch (options.engine) {
31
28
  case 'rnnoise': {
32
29
  const RNNoise = await import('../denoising/RNNoise.js')
33
30
  logger.end()
34
31
 
32
+ const processingSampleRate = 48000
33
+
34
+ logger.start(`Resample audio to ${processingSampleRate} Hz`)
35
+ const inputRawAudioResampled = await resampleAudioSpeex(inputRawAudio, processingSampleRate, 0)
36
+
35
37
  const denoisedAudioChannels: Float32Array[] = []
36
38
 
37
- for (const audioChannel of resampledRawAudio.audioChannels) {
39
+ for (let channelIndex = 0; channelIndex < inputRawAudioResampled.audioChannels.length; channelIndex++) {
40
+ const audioChannel = inputRawAudioResampled.audioChannels[channelIndex]
41
+
38
42
  const audioChannelRawAudio: RawAudio = { audioChannels: [audioChannel], sampleRate: processingSampleRate }
39
43
 
44
+ logger.end()
45
+ logger.logTitledMessage(`Denoise audio channel`, `${channelIndex}`, chalk.magentaBright)
46
+
40
47
  const { denoisedRawAudio, frameVadProbabilities } = await RNNoise.denoiseAudio(audioChannelRawAudio)
48
+
49
+ logger.end()
50
+
41
51
  denoisedAudioChannels.push(denoisedRawAudio.audioChannels[0])
42
52
  }
43
53
 
@@ -46,11 +56,68 @@ export async function denoise(input: AudioSourceParam, options: DenoisingOptions
46
56
  break
47
57
  }
48
58
 
59
+ case 'nsnet2': {
60
+ const NSNet2 = await import('../denoising/NSNet2.js')
61
+ logger.end()
62
+
63
+ const nsnet2Options = options.nsnet2!
64
+
65
+ let processingSampleRate: number
66
+ let packageName: string
67
+
68
+ if (nsnet2Options.model === 'baseline-16khz') {
69
+ processingSampleRate = 16000
70
+
71
+ packageName = 'nsnet2-20ms-baseline'
72
+ } else if (nsnet2Options.model === 'baseline-48khz') {
73
+ processingSampleRate = 48000
74
+
75
+ packageName = 'nsnet2-20ms-48k-baseline'
76
+ } else {
77
+ throw new Error(`Unknown model name: ${nsnet2Options.model}`)
78
+ }
79
+
80
+ if (!nsnet2Options.modelDirectoryPath) {
81
+ nsnet2Options.modelDirectoryPath = await loadPackage(packageName)
82
+ }
83
+
84
+ logger.start(`Resample audio to ${processingSampleRate} Hz`)
85
+ const inputRawAudioResampled = await resampleAudioSpeex(inputRawAudio, processingSampleRate, 0)
86
+
87
+ const denoisedAudioChannels: Float32Array[] = []
88
+
89
+ for (let channelIndex = 0; channelIndex < inputRawAudioResampled.audioChannels.length; channelIndex++) {
90
+ const audioChannel = inputRawAudioResampled.audioChannels[channelIndex]
91
+
92
+ const audioChannelRawAudio: RawAudio = { audioChannels: [audioChannel], sampleRate: processingSampleRate }
93
+
94
+ logger.end()
95
+
96
+ logger.logTitledMessage(`Denoise audio channel`, `${channelIndex}`, chalk.magentaBright)
97
+
98
+ const { denoisedAudio } = await NSNet2.denoiseAudio(audioChannelRawAudio, nsnet2Options)
99
+
100
+ logger.end()
101
+
102
+ denoisedAudioChannels.push(denoisedAudio.audioChannels[0])
103
+ }
104
+
105
+ denoisedAudio = { audioChannels: denoisedAudioChannels, sampleRate: processingSampleRate }
106
+
107
+ break
108
+ }
109
+
49
110
  default: {
50
- throw new Error(`Method: '${options.method}' is not supported`)
111
+ throw new Error(`Engine '${options.engine}' is not recognized.`)
51
112
  }
52
113
  }
53
114
 
115
+ logger.logTitledMessage(`Postprocess`, ``, chalk.magentaBright)
116
+
117
+ logger.start(`Resample denoised audio (${denoisedAudio.sampleRate} Hz) back to original sample rate (${inputRawAudio.sampleRate} Hz)`)
118
+
119
+ denoisedAudio = await ensureRawAudio(denoisedAudio, inputRawAudio.sampleRate, inputRawAudio.audioChannels.length)
120
+
54
121
  logger.start('Postprocess audio')
55
122
 
56
123
  const shouldNormalize = options.postProcessing!.normalizeAudio!
@@ -58,9 +125,11 @@ export async function denoise(input: AudioSourceParam, options: DenoisingOptions
58
125
  const maxGainIncreaseDecibels = options.postProcessing!.maxGainIncrease!
59
126
  const dryMixGainDecibels = options.postProcessing!.dryMixGain!
60
127
 
128
+ attenuateIfClippingInPlace(denoisedAudio)
129
+
61
130
  const preMixPeakDecibels = getSamplePeakDecibels(denoisedAudio.audioChannels)
62
- applyGainDecibelsInPlace(resampledRawAudio, dryMixGainDecibels)
63
- denoisedAudio = mixAudio(denoisedAudio, resampledRawAudio)
131
+ const inputRawAudioWithGain = applyGainDecibels(inputRawAudio, dryMixGainDecibels)
132
+ denoisedAudio = mixAudio(denoisedAudio, inputRawAudioWithGain)
64
133
  const postMixPeakDecibels = getSamplePeakDecibels(denoisedAudio.audioChannels)
65
134
 
66
135
  if (shouldNormalize) {
@@ -85,10 +154,11 @@ export interface DenoisingResult {
85
154
  inputRawAudio: RawAudio
86
155
  }
87
156
 
88
- export type DenoisingMethod = 'rnnoise'
157
+ export type DenoisingEngine = 'rnnoise' | 'nsnet2'
89
158
 
90
159
  export interface DenoisingOptions {
91
- method?: DenoisingMethod,
160
+ engine?: DenoisingEngine,
161
+
92
162
  postProcessing?: {
93
163
  normalizeAudio: boolean
94
164
  targetPeak: number
@@ -96,17 +166,21 @@ export interface DenoisingOptions {
96
166
 
97
167
  dryMixGain?: number
98
168
  }
169
+
170
+ nsnet2?: NSNet2Options
99
171
  }
100
172
 
101
173
  export const defaultDenoisingOptions: DenoisingOptions = {
102
- method: 'rnnoise',
174
+ engine: 'rnnoise',
103
175
 
104
176
  postProcessing: {
105
177
  normalizeAudio: false,
106
178
  targetPeak: -3,
107
179
  maxGainIncrease: 30,
108
- dryMixGain: -20,
109
- }
180
+ dryMixGain: -100,
181
+ },
182
+
183
+ nsnet2: defaultNSNet2Options,
110
184
  }
111
185
 
112
186
  export const denoisingEngines: EngineMetadata[] = [
@@ -115,5 +189,11 @@ export const denoisingEngines: EngineMetadata[] = [
115
189
  name: 'RNNoise',
116
190
  description: 'A noise suppression library based on a recurrent neural network.',
117
191
  type: 'local'
192
+ },
193
+ {
194
+ id: 'nsnet2',
195
+ name: 'Noise Suppression Net 2',
196
+ description: 'Noise suppression models used as baselines for the ICASSP 2021 Deep Noise Suppression challenge.',
197
+ type: 'local'
118
198
  }
119
199
  ]
@@ -5,15 +5,13 @@ import { loadPackage } from '../utilities/PackageManager.js';
5
5
  import { EngineMetadata } from './Common.js';
6
6
  import chalk from 'chalk';
7
7
  import { readdir } from '../utilities/FileSystem.js';
8
- import path from 'node:path';
9
8
  import { defaultMDXNetOptions, getProfileForMDXNetModelName, MDXNetOptions } from '../source-separation/MDXNetSourceSeparation.js';
9
+ import { joinPath } from '../utilities/PathUtilities.js';
10
10
 
11
11
  export async function isolate(input: AudioSourceParam, options: SourceSeparationOptions): Promise<SourceSeparationResult> {
12
12
  const logger = new Logger()
13
13
  const startTimestamp = logger.getTimestamp()
14
14
 
15
- await logger.startAsync('Prepare for source separation')
16
-
17
15
  const inputRawAudio = await ensureRawAudio(input)
18
16
 
19
17
  let isolatedRawAudio: RawAudio
@@ -34,22 +32,27 @@ export async function isolate(input: AudioSourceParam, options: SourceSeparation
34
32
  throw new Error(`Couldn't find an ONNX model file in package directory`)
35
33
  }
36
34
 
37
- const modelPath = path.join(packageDir, modelFilename)
35
+ const modelPath = joinPath(packageDir, modelFilename)
38
36
 
39
37
  await logger.startAsync(`Convert audio to 44.1 kHz stereo`)
40
38
 
41
- const inputRawAudio44100Stereo = await ensureRawAudio(inputRawAudio, 44100, 2)
39
+ let inputRawAudioAs44100Stereo: RawAudio | undefined = await ensureRawAudio(inputRawAudio, 44100, 2)
42
40
 
43
41
  logger.end()
44
42
 
45
43
  const modelProfile = getProfileForMDXNetModelName(mdxNetOptions.model!)
46
44
 
47
- isolatedRawAudio = await MDXNetSourceSeparation.isolate(inputRawAudio44100Stereo, modelPath, modelProfile, mdxNetOptions)
48
-
45
+ isolatedRawAudio = await MDXNetSourceSeparation.isolate(inputRawAudioAs44100Stereo, modelPath, modelProfile, mdxNetOptions)
49
46
  logger.end()
50
47
 
48
+ // Release memory for the converted input audio since it's not needed anymore
49
+ inputRawAudioAs44100Stereo = undefined
50
+
51
+ await logger.startAsync(`Convert isolated audio to back original sample rate (${inputRawAudio.sampleRate} Hz) and channel count (${inputRawAudio.audioChannels.length})`)
52
+ isolatedRawAudio = await ensureRawAudio(isolatedRawAudio, inputRawAudio.sampleRate, inputRawAudio.audioChannels.length)
53
+
51
54
  await logger.startAsync(`Subtract from original waveform to extract background audio`)
52
- backgroundRawAudio = subtractAudio(inputRawAudio44100Stereo, isolatedRawAudio)
55
+ backgroundRawAudio = subtractAudio(inputRawAudio, isolatedRawAudio)
53
56
 
54
57
  break
55
58
  }
@@ -5,7 +5,6 @@ import { Logger } from '../utilities/Logger.js'
5
5
 
6
6
  import * as API from './API.js'
7
7
  import { logToStderr } from '../utilities/Utilities.js'
8
- import path from 'path'
9
8
  import { type WhisperLanguageDetectionOptions } from '../recognition/WhisperSTT.js'
10
9
  import { formatLanguageCodeWithName, languageCodeToName } from '../utilities/Locale.js'
11
10
  import { loadPackage } from '../utilities/PackageManager.js'
@@ -14,6 +13,7 @@ import { type WhisperCppOptions } from '../recognition/WhisperCppSTT.js'
14
13
  import { type SileroLanguageDetectionOptions } from '../speech-language-detection/SileroLanguageDetection.js'
15
14
  import { OnnxExecutionProvider } from '../utilities/OnnxUtilities.js'
16
15
  import { LanguageDetectionResults } from './LanguageDetectionCommon.js'
16
+ import { joinPath } from '../utilities/PathUtilities.js'
17
17
 
18
18
  const log = logToStderr
19
19
 
@@ -55,9 +55,9 @@ export async function detectSpeechLanguage(input: AudioSourceParam, options: Spe
55
55
 
56
56
  const modelDir = await loadPackage('silero-lang-classifier-95')
57
57
 
58
- const modelPath = path.join(modelDir, 'lang_classifier_95.onnx')
59
- const languageDictionaryPath = path.join(modelDir, 'lang_dict_95.json')
60
- const languageGroupDictionaryPath = path.join(modelDir, 'lang_group_dict_95.json')
58
+ const modelPath = joinPath(modelDir, 'lang_classifier_95.onnx')
59
+ const languageDictionaryPath = joinPath(modelDir, 'lang_dict_95.json')
60
+ const languageGroupDictionaryPath = joinPath(modelDir, 'lang_group_dict_95.json')
61
61
  const onnxExecutionProviders: OnnxExecutionProvider[] = sileroOptions.provider ? [sileroOptions.provider] : []
62
62
 
63
63
  const languageResults = await SileroLanguageDetection.detectLanguage(
@@ -1,5 +1,3 @@
1
- import path from 'node:path'
2
-
3
1
  import { deepClone, extendDeep } from '../utilities/ObjectUtilities.js'
4
2
 
5
3
  import * as FFMpegTranscoder from '../codecs/FFMpegTranscoder.js'
@@ -27,6 +25,8 @@ import { type ElevenlabsTTSOptions } from '../synthesis/ElevenlabsTTS.js'
27
25
  import { OnnxExecutionProvider } from '../utilities/OnnxUtilities.js'
28
26
  import { simplifyPunctuationCharacters } from '../nlp/TextNormalizer.js'
29
27
  import { convertHtmlToText } from '../utilities/StringUtilities.js'
28
+ import { joinPath, resolvePath } from '../utilities/PathUtilities.js'
29
+ import { Timer } from '../utilities/Timer.js'
30
30
 
31
31
  const log = logToStderr
32
32
 
@@ -55,6 +55,8 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
55
55
  const logger = new Logger()
56
56
  options = extendDeep(defaultSynthesisOptions, options)
57
57
 
58
+ const synthesisStartTime = Timer.currentTime
59
+
58
60
  if (!options.language && !options.voice) {
59
61
  logger.start('No language or voice specified. Detect language')
60
62
 
@@ -261,7 +263,7 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
261
263
  async function convertToTargetCodecIfNeeded(rawAudio: RawAudio) {
262
264
  const targetCodec = options.outputAudioFormat?.codec
263
265
 
264
- let output: RawAudio | Buffer
266
+ let output: RawAudio | Uint8Array
265
267
 
266
268
  if (targetCodec) {
267
269
  logger.start(`Convert to ${targetCodec} codec`)
@@ -283,6 +285,8 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
283
285
 
284
286
  logger.end()
285
287
 
288
+ logger.logDuration('Total synthesis time', synthesisStartTime, chalk.magentaBright)
289
+
286
290
  return {
287
291
  audio: resultAudio,
288
292
  timeline,
@@ -292,7 +296,7 @@ async function synthesizeSegments(segments: string[], options: SynthesisOptions,
292
296
  }
293
297
 
294
298
  export interface SynthesisResult {
295
- audio: RawAudio | Buffer
299
+ audio: RawAudio | Uint8Array
296
300
  timeline: Timeline
297
301
  language: string
298
302
  voice: string
@@ -427,8 +431,8 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
427
431
 
428
432
  const { textAnalysisFilename, signalGenerationFilename } = SvoxPicoTTS.getResourceFilenamesForLanguage(language)
429
433
 
430
- const resourceFilePath = path.resolve(voicePackagePath!, textAnalysisFilename)
431
- const signalGenerationFilePath = path.resolve(voicePackagePath!, signalGenerationFilename)
434
+ const resourceFilePath = resolvePath(voicePackagePath!, textAnalysisFilename)
435
+ const signalGenerationFilePath = resolvePath(voicePackagePath!, signalGenerationFilename)
432
436
 
433
437
  const { rawAudio } = await SvoxPicoTTS.synthesize(preparedText, resourceFilePath, signalGenerationFilePath)
434
438
 
@@ -955,7 +959,7 @@ async function synthesizeSegment(text: string, options: SynthesisOptions) {
955
959
 
956
960
  logger.end()
957
961
 
958
- logger.logDuration('Total synthesis time', startTimestamp, chalk.magentaBright)
962
+ logger.logDuration('Segment synthesis time', startTimestamp, chalk.magentaBright)
959
963
 
960
964
  return { synthesizedAudio, timeline }
961
965
  }
@@ -1273,11 +1277,11 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
1273
1277
 
1274
1278
  if (!cacheDir) {
1275
1279
  const appDataDir = getAppDataDir(appName)
1276
- cacheDir = path.join(appDataDir, 'voice-list-cache')
1280
+ cacheDir = joinPath(appDataDir, 'voice-list-cache')
1277
1281
  await ensureDir(cacheDir)
1278
1282
  }
1279
1283
 
1280
- const cacheFilePath = path.join(cacheDir, `${options.engine}.voices.json`)
1284
+ const cacheFilePath = joinPath(cacheDir, `${options.engine}.voices.json`)
1281
1285
 
1282
1286
  async function loadVoiceList() {
1283
1287
  let voiceList: SynthesisVoice[] = []
@@ -1544,7 +1548,7 @@ export async function requestVoiceList(options: VoiceListRequestOptions): Promis
1544
1548
 
1545
1549
 
1546
1550
  if (cacheFilePath) {
1547
- await writeFileSafe(cacheFilePath, stringifyAndFormatJson(voiceList))
1551
+ await writeFileSafe(cacheFilePath, await stringifyAndFormatJson(voiceList))
1548
1552
  }
1549
1553
 
1550
1554
  return voiceList
@@ -1671,7 +1675,7 @@ export const defaultVoiceListRequestOptions: VoiceListRequestOptions = {
1671
1675
  export interface SynthesisSegmentEventData {
1672
1676
  index: number
1673
1677
  total: number
1674
- audio: RawAudio | Buffer
1678
+ audio: RawAudio | Uint8Array
1675
1679
  timeline: Timeline
1676
1680
  transcript: string
1677
1681
  language: string
@@ -5,13 +5,13 @@ import { AudioSourceParam, RawAudio, cropToTimeline, ensureRawAudio, } from '../
5
5
  import { Logger } from '../utilities/Logger.js'
6
6
 
7
7
  import { Timeline } from '../utilities/Timeline.js'
8
- import path from 'path'
9
8
  import { loadPackage } from '../utilities/PackageManager.js'
10
9
  import { EngineMetadata } from './Common.js'
11
10
  import chalk from 'chalk'
12
11
  import { type AdaptiveGateVADOptions } from '../voice-activity-detection/AdaptiveGateVAD.js'
13
12
  import { type WhisperVADOptions } from '../recognition/WhisperSTT.js'
14
13
  import { OnnxExecutionProvider } from '../utilities/OnnxUtilities.js'
14
+ import { joinPath } from '../utilities/PathUtilities.js'
15
15
 
16
16
  const log = logToStderr
17
17
 
@@ -54,7 +54,7 @@ export async function detectVoiceActivity(input: AudioSourceParam, options: VADO
54
54
 
55
55
  const modelDir = await loadPackage('silero-vad')
56
56
 
57
- const modelPath = path.join(modelDir, 'silero-vad.onnx')
57
+ const modelPath = joinPath(modelDir, 'silero-vad.onnx')
58
58
  const frameDuration = sileroOptions.frameDuration!
59
59
 
60
60
  const onnxExecutionProviders: OnnxExecutionProvider[] = sileroOptions.provider ? [sileroOptions.provider] : []