echogarden 1.8.7 → 2.0.1

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 (309) 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 +135 -49
  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/encodings/HtmlEscape.d.ts +1 -0
  75. package/dist/encodings/HtmlEscape.js +30 -0
  76. package/dist/encodings/HtmlEscape.js.map +1 -0
  77. package/dist/{utilities → encodings}/LEB128.d.ts +1 -1
  78. package/dist/{utilities → encodings}/LEB128.js +4 -4
  79. package/dist/encodings/LEB128.js.map +1 -0
  80. package/dist/{utilities → encodings}/LPVarInt.d.ts +1 -1
  81. package/dist/{utilities → encodings}/LPVarInt.js +6 -6
  82. package/dist/encodings/LPVarInt.js.map +1 -0
  83. package/dist/encodings/TextEncodingsCommon.d.ts +4 -0
  84. package/dist/encodings/TextEncodingsCommon.js +2 -0
  85. package/dist/encodings/TextEncodingsCommon.js.map +1 -0
  86. package/dist/encodings/Utf16.d.ts +10 -0
  87. package/dist/encodings/Utf16.js +36 -0
  88. package/dist/encodings/Utf16.js.map +1 -0
  89. package/dist/encodings/Utf32.d.ts +4 -0
  90. package/dist/encodings/Utf32.js +32 -0
  91. package/dist/encodings/Utf32.js.map +1 -0
  92. package/dist/encodings/Utf8.d.ts +10 -0
  93. package/dist/encodings/Utf8.js +97 -0
  94. package/dist/encodings/Utf8.js.map +1 -0
  95. package/dist/math/MedianFilter.d.ts +0 -1
  96. package/dist/math/MedianFilter.js +0 -10
  97. package/dist/math/MedianFilter.js.map +1 -1
  98. package/dist/nlp/JapaneseSegmentation.js +2 -2
  99. package/dist/nlp/JapaneseSegmentation.js.map +1 -1
  100. package/dist/recognition/GoogleCloudSTT.js +2 -1
  101. package/dist/recognition/GoogleCloudSTT.js.map +1 -1
  102. package/dist/recognition/SileroSTT.js +3 -3
  103. package/dist/recognition/SileroSTT.js.map +1 -1
  104. package/dist/recognition/VoskSTT.js +3 -3
  105. package/dist/recognition/VoskSTT.js.map +1 -1
  106. package/dist/recognition/WhisperCppSTT.js +4 -5
  107. package/dist/recognition/WhisperCppSTT.js.map +1 -1
  108. package/dist/recognition/WhisperSTT.js +8 -7
  109. package/dist/recognition/WhisperSTT.js.map +1 -1
  110. package/dist/server/Client.d.ts +1 -1
  111. package/dist/server/Client.js +2 -1
  112. package/dist/server/Client.js.map +1 -1
  113. package/dist/server/Server.js +5 -4
  114. package/dist/server/Server.js.map +1 -1
  115. package/dist/server/Worker.js +1 -1
  116. package/dist/server/Worker.js.map +1 -1
  117. package/dist/source-separation/MDXNetSourceSeparation.js +1 -1
  118. package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
  119. package/dist/speech-search/DTWSpeechSearch.d.ts +1 -1
  120. package/dist/speech-search/DTWSpeechSearch.js +25 -6
  121. package/dist/speech-search/DTWSpeechSearch.js.map +1 -1
  122. package/dist/subtitles/Subtitles.js +2 -2
  123. package/dist/subtitles/Subtitles.js.map +1 -1
  124. package/dist/synthesis/AzureCognitiveServicesTTS.js +19 -20
  125. package/dist/synthesis/AzureCognitiveServicesTTS.js.map +1 -1
  126. package/dist/synthesis/CoquiServerTTS.js +1 -1
  127. package/dist/synthesis/CoquiServerTTS.js.map +1 -1
  128. package/dist/synthesis/ElevenlabsTTS.js +1 -1
  129. package/dist/synthesis/ElevenlabsTTS.js.map +1 -1
  130. package/dist/synthesis/EspeakTTS.d.ts +0 -1
  131. package/dist/synthesis/EspeakTTS.js +2 -11
  132. package/dist/synthesis/EspeakTTS.js.map +1 -1
  133. package/dist/synthesis/FliteTTS.js +8 -9
  134. package/dist/synthesis/FliteTTS.js.map +1 -1
  135. package/dist/synthesis/GoogleCloudTTS.d.ts +1 -1
  136. package/dist/synthesis/GoogleCloudTTS.js +2 -1
  137. package/dist/synthesis/GoogleCloudTTS.js.map +1 -1
  138. package/dist/synthesis/GoogleTranslateTTS.d.ts +1 -1
  139. package/dist/synthesis/GoogleTranslateTTS.js +4 -3
  140. package/dist/synthesis/GoogleTranslateTTS.js.map +1 -1
  141. package/dist/synthesis/MicrosoftEdgeTTS.js +34 -9
  142. package/dist/synthesis/MicrosoftEdgeTTS.js.map +1 -1
  143. package/dist/synthesis/SapiTTS.js +1 -1
  144. package/dist/synthesis/SapiTTS.js.map +1 -1
  145. package/dist/synthesis/StreamlabsPollyTTS.d.ts +1 -1
  146. package/dist/synthesis/StreamlabsPollyTTS.js +1 -1
  147. package/dist/synthesis/StreamlabsPollyTTS.js.map +1 -1
  148. package/dist/synthesis/SvoxPicoTTS.js +6 -6
  149. package/dist/synthesis/SvoxPicoTTS.js.map +1 -1
  150. package/dist/synthesis/VitsTTS.js +2 -2
  151. package/dist/synthesis/VitsTTS.js.map +1 -1
  152. package/dist/tests/Test.js +1 -47
  153. package/dist/tests/Test.js.map +1 -1
  154. package/dist/text-language-detection/FastTextLanguageDetection.js +2 -2
  155. package/dist/text-language-detection/FastTextLanguageDetection.js.map +1 -1
  156. package/dist/text-translation/GoogleTranslateTextTranslation.js +1 -1
  157. package/dist/text-translation/GoogleTranslateTextTranslation.js.map +1 -1
  158. package/dist/typings/TypedArray.d.ts +4 -0
  159. package/dist/typings/TypedArray.js +2 -0
  160. package/dist/typings/TypedArray.js.map +1 -0
  161. package/dist/utilities/BinaryArrayConversion.d.ts +12 -22
  162. package/dist/utilities/BinaryArrayConversion.js +40 -90
  163. package/dist/utilities/BinaryArrayConversion.js.map +1 -1
  164. package/dist/utilities/BinaryUtilities.d.ts +13 -0
  165. package/dist/utilities/BinaryUtilities.js +113 -0
  166. package/dist/utilities/BinaryUtilities.js.map +1 -0
  167. package/dist/utilities/Compression.js +7 -6
  168. package/dist/utilities/Compression.js.map +1 -1
  169. package/dist/utilities/FileDownloader.js +26 -47
  170. package/dist/utilities/FileDownloader.js.map +1 -1
  171. package/dist/utilities/FileReader.d.ts +14 -0
  172. package/dist/utilities/FileReader.js +71 -0
  173. package/dist/utilities/FileReader.js.map +1 -0
  174. package/dist/utilities/FileSystem.d.ts +19 -16
  175. package/dist/utilities/FileSystem.js +170 -109
  176. package/dist/utilities/FileSystem.js.map +1 -1
  177. package/dist/utilities/FileWriter.d.ts +12 -0
  178. package/dist/utilities/FileWriter.js +60 -0
  179. package/dist/utilities/FileWriter.js.map +1 -0
  180. package/dist/utilities/NpmUtilities.d.ts +1 -0
  181. package/dist/utilities/NpmUtilities.js +14 -0
  182. package/dist/utilities/NpmUtilities.js.map +1 -0
  183. package/dist/utilities/PackageManager.js +7 -5
  184. package/dist/utilities/PackageManager.js.map +1 -1
  185. package/dist/utilities/PathUtilities.d.ts +8 -0
  186. package/dist/utilities/PathUtilities.js +28 -7
  187. package/dist/utilities/PathUtilities.js.map +1 -1
  188. package/dist/utilities/SignalChannel.d.ts +3 -1
  189. package/dist/utilities/SignalChannel.js +9 -5
  190. package/dist/utilities/SignalChannel.js.map +1 -1
  191. package/dist/utilities/StringBuilder.d.ts +10 -0
  192. package/dist/utilities/StringBuilder.js +39 -0
  193. package/dist/utilities/StringBuilder.js.map +1 -0
  194. package/dist/utilities/StringUtilities.js.map +1 -1
  195. package/dist/utilities/TarballMaker.js +6 -7
  196. package/dist/utilities/TarballMaker.js.map +1 -1
  197. package/dist/utilities/Timer.js +2 -2
  198. package/dist/utilities/Timer.js.map +1 -1
  199. package/dist/utilities/Utilities.d.ts +11 -5
  200. package/dist/utilities/Utilities.js +69 -33
  201. package/dist/utilities/Utilities.js.map +1 -1
  202. package/dist/utilities/VirtualFileReadStream.d.ts +3 -3
  203. package/dist/utilities/VirtualFileReadStream.js.map +1 -1
  204. package/dist/utilities/WasmMemoryManager.d.ts +5 -2
  205. package/dist/utilities/WasmMemoryManager.js +15 -4
  206. package/dist/utilities/WasmMemoryManager.js.map +1 -1
  207. package/docs/API.md +1 -1
  208. package/docs/CLI.md +1 -1
  209. package/docs/Licenses.md +3 -0
  210. package/docs/Options.md +9 -3
  211. package/docs/Tasklist.md +0 -1
  212. package/docs/Technical.md +2 -2
  213. package/package.json +22 -25
  214. package/src/alignment/DTWSequenceAlignment.ts +5 -5
  215. package/src/alignment/SpeechAlignment.ts +6 -6
  216. package/src/api/Alignment.ts +65 -12
  217. package/src/api/Denoising.ts +97 -17
  218. package/src/api/SourceSeparation.ts +11 -8
  219. package/src/api/SpeechLanguageDetection.ts +4 -4
  220. package/src/api/Synthesis.ts +15 -11
  221. package/src/api/VoiceActivityDetection.ts +2 -2
  222. package/src/audio/AudioBufferConversion.ts +84 -45
  223. package/src/audio/AudioPlayer.ts +155 -53
  224. package/src/audio/AudioUtilities.ts +38 -22
  225. package/src/audio/SoxPath.ts +3 -3
  226. package/src/build-tools/MakeTarballsForInstalledPackages.ts +3 -3
  227. package/src/cli/CLI.ts +30 -36
  228. package/src/cli/CLIConfigFile.ts +2 -2
  229. package/src/cli/CLIOptions.ts +4 -1
  230. package/src/codecs/FFMpegTranscoder.ts +11 -11
  231. package/src/codecs/TIMITCodec.ts +5 -4
  232. package/src/codecs/WaveCodec.ts +42 -39
  233. package/src/data-structures/DynamicTypedArray.ts +64 -0
  234. package/src/denoising/NSNet2.ts +182 -0
  235. package/src/denoising/RNNoise.ts +3 -3
  236. package/src/dsp/FFT.ts +21 -1
  237. package/src/encodings/Ascii.ts +60 -0
  238. package/src/encodings/Base64.ts +157 -0
  239. package/src/encodings/Hex.ts +67 -0
  240. package/src/encodings/HtmlEscape.ts +39 -0
  241. package/src/{utilities → encodings}/LEB128.ts +4 -4
  242. package/src/{utilities → encodings}/LPVarInt.ts +6 -6
  243. package/src/encodings/TextEncodingsCommon.ts +4 -0
  244. package/src/encodings/Utf16.ts +52 -0
  245. package/src/encodings/Utf32.ts +44 -0
  246. package/src/encodings/Utf8.ts +117 -0
  247. package/src/math/MedianFilter.ts +0 -16
  248. package/src/nlp/JapaneseSegmentation.ts +2 -2
  249. package/src/recognition/GoogleCloudSTT.ts +2 -1
  250. package/src/recognition/SileroSTT.ts +3 -3
  251. package/src/recognition/VoskSTT.ts +3 -3
  252. package/src/recognition/WhisperCppSTT.ts +4 -5
  253. package/src/recognition/WhisperSTT.ts +8 -7
  254. package/src/server/Client.ts +4 -3
  255. package/src/server/Server.ts +6 -5
  256. package/src/server/Worker.ts +1 -1
  257. package/src/source-separation/MDXNetSourceSeparation.ts +1 -1
  258. package/src/speech-search/DTWSpeechSearch.ts +36 -7
  259. package/src/subtitles/Subtitles.ts +3 -3
  260. package/src/synthesis/AzureCognitiveServicesTTS.ts +20 -18
  261. package/src/synthesis/CoquiServerTTS.ts +2 -2
  262. package/src/synthesis/ElevenlabsTTS.ts +1 -1
  263. package/src/synthesis/EspeakTTS.ts +2 -18
  264. package/src/synthesis/FliteTTS.ts +8 -10
  265. package/src/synthesis/GoogleCloudTTS.ts +3 -2
  266. package/src/synthesis/GoogleTranslateTTS.ts +5 -4
  267. package/src/synthesis/MicrosoftEdgeTTS.ts +49 -18
  268. package/src/synthesis/SapiTTS.ts +1 -1
  269. package/src/synthesis/StreamlabsPollyTTS.ts +1 -1
  270. package/src/synthesis/SvoxPicoTTS.ts +8 -8
  271. package/src/synthesis/VitsTTS.ts +2 -2
  272. package/src/tests/Test.ts +1 -60
  273. package/src/text-language-detection/FastTextLanguageDetection.ts +2 -2
  274. package/src/text-translation/GoogleTranslateTextTranslation.ts +1 -1
  275. package/src/typings/Fillers.d.ts +2 -10
  276. package/src/typings/TypedArray.ts +5 -0
  277. package/src/utilities/BinaryArrayConversion.ts +51 -125
  278. package/src/utilities/BinaryUtilities.ts +138 -0
  279. package/src/utilities/Compression.ts +7 -6
  280. package/src/utilities/FileDownloader.ts +27 -52
  281. package/src/utilities/FileReader.ts +89 -0
  282. package/src/utilities/FileSystem.ts +213 -120
  283. package/src/utilities/FileWriter.ts +72 -0
  284. package/src/utilities/NpmUtilities.ts +23 -0
  285. package/src/utilities/PackageManager.ts +8 -5
  286. package/src/utilities/PathUtilities.ts +36 -7
  287. package/src/utilities/SignalChannel.ts +14 -7
  288. package/src/utilities/StringBuilder.ts +45 -0
  289. package/src/utilities/StringUtilities.ts +2 -0
  290. package/src/utilities/TarballMaker.ts +6 -7
  291. package/src/utilities/Timer.ts +4 -2
  292. package/src/utilities/Utilities.ts +80 -45
  293. package/src/utilities/VirtualFileReadStream.ts +5 -5
  294. package/src/utilities/WasmMemoryManager.ts +20 -6
  295. package/tsconfig.json +96 -0
  296. package/dist/utilities/DynamicUint8Array.d.ts +0 -9
  297. package/dist/utilities/DynamicUint8Array.js +0 -31
  298. package/dist/utilities/DynamicUint8Array.js.map +0 -1
  299. package/dist/utilities/LEB128.js.map +0 -1
  300. package/dist/utilities/LPVarInt.js.map +0 -1
  301. package/dist/utilities/Queue.js.map +0 -1
  302. package/dist/utilities/WindowedList.js.map +0 -1
  303. package/src/utilities/DynamicUint8Array.ts +0 -39
  304. /package/dist/{utilities → data-structures}/Queue.d.ts +0 -0
  305. /package/dist/{utilities → data-structures}/Queue.js +0 -0
  306. /package/dist/{utilities → data-structures}/WindowedList.d.ts +0 -0
  307. /package/dist/{utilities → data-structures}/WindowedList.js +0 -0
  308. /package/src/{utilities → data-structures}/Queue.ts +0 -0
  309. /package/src/{utilities → data-structures}/WindowedList.ts +0 -0
@@ -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 totalSynthesisTimeTimer = new Timer()
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.logTitledMessage('Total synthesis time', `${totalSynthesisTimeTimer.elapsedTime.toFixed(1)}ms`, 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] : []
@@ -5,7 +5,7 @@ import { BitDepth, SampleFormat } from '../codecs/WaveCodec.js'
5
5
  /////////////////////////////////////////////////////////////////////////////////////////////
6
6
  // Low level audio sample conversions
7
7
  /////////////////////////////////////////////////////////////////////////////////////////////
8
- export function encodeToAudioBuffer(audioChannels: Float32Array[], targetBitDepth: BitDepth = 16, targetSampleFormat: SampleFormat = SampleFormat.PCM) {
8
+ export function encodeToAudioBuffer(audioChannels: Float32Array[], targetBitDepth: BitDepth = 16, targetSampleFormat: SampleFormat = SampleFormat.PCM): Uint8Array {
9
9
  const interleavedChannels = interleaveChannels(audioChannels)
10
10
 
11
11
  audioChannels = [] // Zero the array references to allow the GC to free up memory, if possible
@@ -32,13 +32,13 @@ export function encodeToAudioBuffer(audioChannels: Float32Array[], targetBitDept
32
32
  }
33
33
  } else if (targetSampleFormat === SampleFormat.Alaw) {
34
34
  if (targetBitDepth === 8) {
35
- return Buffer.from(AlawMulaw.alaw.encode(float32ToInt16Pcm(interleavedChannels)))
35
+ return AlawMulaw.alaw.encode(float32ToInt16Pcm(interleavedChannels))
36
36
  } else {
37
37
  throw new Error(`Unsupported alaw bit depth: ${targetBitDepth}`)
38
38
  }
39
39
  } else if (targetSampleFormat === SampleFormat.Mulaw) {
40
40
  if (targetBitDepth === 8) {
41
- return Buffer.from(AlawMulaw.mulaw.encode(float32ToInt16Pcm(interleavedChannels)))
41
+ return AlawMulaw.mulaw.encode(float32ToInt16Pcm(interleavedChannels))
42
42
  } else {
43
43
  throw new Error(`Unsupported mulaw bit depth: ${targetBitDepth}`)
44
44
  }
@@ -47,7 +47,7 @@ export function encodeToAudioBuffer(audioChannels: Float32Array[], targetBitDept
47
47
  }
48
48
  }
49
49
 
50
- export function decodeToChannels(audioBuffer: Buffer, channelCount: number, sourceBitDepth: number, sourceSampleFormat: SampleFormat) {
50
+ export function decodeToChannels(audioBuffer: Uint8Array, channelCount: number, sourceBitDepth: number, sourceSampleFormat: SampleFormat) {
51
51
  let interleavedChannels: Float32Array
52
52
 
53
53
  if (sourceSampleFormat === SampleFormat.PCM) {
@@ -86,29 +86,39 @@ export function decodeToChannels(audioBuffer: Buffer, channelCount: number, sour
86
86
  throw new Error(`Unsupported audio format: ${sourceSampleFormat}`)
87
87
  }
88
88
 
89
- audioBuffer = Buffer.from([]) // Zero the buffer reference to allow the GC to free up memory, if possible
89
+ audioBuffer = new Uint8Array(0) // Zero the buffer reference to allow the GC to free up memory, if possible
90
90
 
91
91
  return deInterleaveChannels(interleavedChannels, channelCount)
92
92
  }
93
93
 
94
94
  // Int8 PCM <-> Float32 conversion
95
95
  export function int8PcmToFloat32(input: Int8Array) {
96
- const output = new Float32Array(input.length)
96
+ const sampleCount = input.length
97
97
 
98
- for (let i = 0; i < input.length; i++) {
99
- const sample = input[i]
100
- output[i] = sample < 0 ? sample / 128 : sample / 127
98
+ const output = new Float32Array(sampleCount)
99
+
100
+ for (let i = 0; i < sampleCount; i++) {
101
+ output[i] = input[i] / 128
101
102
  }
102
103
 
103
104
  return output
104
105
  }
105
106
 
106
107
  export function float32ToInt8Pcm(input: Float32Array) {
107
- const output = new Int8Array(input.length)
108
+ const sampleCount = input.length
109
+
110
+ const output = new Int8Array(sampleCount)
111
+
112
+ for (let i = 0; i < sampleCount; i++) {
113
+ const int8Sample = input[i] * 128
108
114
 
109
- for (let i = 0; i < input.length; i++) {
110
- const sample = clampFloatSample(input[i])
111
- output[i] = (sample < 0 ? sample * 128 : sample * 127) | 0
115
+ if (int8Sample < -128) {
116
+ output[i] = -128
117
+ } else if (int8Sample > 127) {
118
+ output[i] = 127
119
+ } else {
120
+ output[i] = int8Sample
121
+ }
112
122
  }
113
123
 
114
124
  return output
@@ -116,22 +126,32 @@ export function float32ToInt8Pcm(input: Float32Array) {
116
126
 
117
127
  // Int16 PCM <-> Float32 conversion
118
128
  export function int16PcmToFloat32(input: Int16Array) {
119
- const output = new Float32Array(input.length)
129
+ const sampleCount = input.length
130
+
131
+ const output = new Float32Array(sampleCount)
120
132
 
121
- for (let i = 0; i < input.length; i++) {
122
- const sample = input[i]
123
- output[i] = sample < 0 ? sample / 32768 : sample / 32767
133
+ for (let i = 0; i < sampleCount; i++) {
134
+ output[i] = input[i] / 32768
124
135
  }
125
136
 
126
137
  return output
127
138
  }
128
139
 
129
140
  export function float32ToInt16Pcm(input: Float32Array) {
130
- const output = new Int16Array(input.length)
141
+ const sampleCount = input.length
142
+
143
+ const output = new Int16Array(sampleCount)
144
+
145
+ for (let i = 0; i < sampleCount; i++) {
146
+ const int16Sample = input[i] * 32768
131
147
 
132
- for (let i = 0; i < input.length; i++) {
133
- const sample = clampFloatSample(input[i])
134
- output[i] = (sample < 0 ? sample * 32768 : sample * 32767) | 0
148
+ if (int16Sample < -32768) {
149
+ output[i] = -32768
150
+ } else if (int16Sample > 32767) {
151
+ output[i] = 32767
152
+ } else {
153
+ output[i] = int16Sample
154
+ }
135
155
  }
136
156
 
137
157
  return output
@@ -139,22 +159,32 @@ export function float32ToInt16Pcm(input: Float32Array) {
139
159
 
140
160
  // Int24 PCM <-> Float32 conversion (uses int32 for storage)
141
161
  export function int24PcmToFloat32(input: Int32Array) {
142
- const output = new Float32Array(input.length)
162
+ const sampleCount = input.length
143
163
 
144
- for (let i = 0; i < input.length; i++) {
145
- const sample = input[i]
146
- output[i] = sample < 0 ? sample / 8388608 : sample / 8388607
164
+ const output = new Float32Array(sampleCount)
165
+
166
+ for (let i = 0; i < sampleCount; i++) {
167
+ output[i] = input[i] / 8388608
147
168
  }
148
169
 
149
170
  return output
150
171
  }
151
172
 
152
173
  export function float32ToInt24Pcm(input: Float32Array) {
153
- const output = new Int32Array(input.length)
174
+ const sampleCount = input.length
154
175
 
155
- for (let i = 0; i < input.length; i++) {
156
- const sample = clampFloatSample(input[i])
157
- output[i] = (sample < 0 ? sample * 8388608 : sample * 8388607) | 0
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
+ }
158
188
  }
159
189
 
160
190
  return output
@@ -162,22 +192,32 @@ export function float32ToInt24Pcm(input: Float32Array) {
162
192
 
163
193
  // Int32 PCM <-> Float32 conversion
164
194
  export function int32PcmToFloat32(input: Int32Array) {
165
- const output = new Float32Array(input.length)
195
+ const sampleCount = input.length
196
+
197
+ const output = new Float32Array(sampleCount)
166
198
 
167
- for (let i = 0; i < input.length; i++) {
168
- const sample = input[i]
169
- output[i] = sample < 0 ? sample / 2147483648 : sample / 2147483647
199
+ for (let i = 0; i < sampleCount; i++) {
200
+ output[i] = input[i] / 2147483648
170
201
  }
171
202
 
172
203
  return output
173
204
  }
174
205
 
175
206
  export function float32ToInt32Pcm(input: Float32Array) {
176
- const output = new Int32Array(input.length)
207
+ const sampleCount = input.length
208
+
209
+ const output = new Int32Array(sampleCount)
177
210
 
178
- for (let i = 0; i < input.length; i++) {
179
- const sample = clampFloatSample(input[i])
180
- output[i] = (sample < 0 ? sample * 2147483648 : sample * 2147483647) | 0
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
+ }
181
221
  }
182
222
 
183
223
  return output
@@ -202,10 +242,9 @@ export function interleaveChannels(channels: Float32Array[]) {
202
242
 
203
243
  let writeIndex = 0
204
244
 
205
- for (let i = 0; i < sampleCount; i++) {
206
- for (let c = 0; c < channelCount; c++) {
207
- result[writeIndex] = channels[c][i]
208
- writeIndex += 1
245
+ for (let sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) {
246
+ for (let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
247
+ result[writeIndex++] = channels[channelIndex][sampleIndex]
209
248
  }
210
249
  }
211
250
 
@@ -226,6 +265,7 @@ export function deInterleaveChannels(interleavedChannels: Float32Array, channelC
226
265
  }
227
266
 
228
267
  const sampleCount = interleavedChannels.length / channelCount
268
+
229
269
  const channels: Float32Array[] = []
230
270
 
231
271
  for (let i = 0; i < channelCount; i++) {
@@ -234,10 +274,9 @@ export function deInterleaveChannels(interleavedChannels: Float32Array, channelC
234
274
 
235
275
  let readIndex = 0
236
276
 
237
- for (let i = 0; i < sampleCount; i++) {
238
- for (let c = 0; c < channelCount; c++) {
239
- channels[c][i] = interleavedChannels[readIndex]
240
- readIndex += 1
277
+ for (let sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++) {
278
+ for (let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
279
+ channels[channelIndex][sampleIndex] = interleavedChannels[readIndex++]
241
280
  }
242
281
  }
243
282