echogarden 2.10.2 → 3.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 (496) hide show
  1. package/LICENSE.MIT.md +7 -0
  2. package/README.md +9 -1
  3. package/data/lexicons/heteronyms.en.json +44 -10
  4. package/data/lexicons/words.en.json +726 -22
  5. package/data/schemas/options.json +91 -153
  6. package/dist/alignment/DTWMfccSequenceAlignment.js.map +1 -1
  7. package/dist/alignment/SemanticTextAlignment.d.ts +4 -2
  8. package/dist/alignment/SemanticTextAlignment.d.ts.map +1 -1
  9. package/dist/alignment/SemanticTextAlignment.js +7 -7
  10. package/dist/alignment/SemanticTextAlignment.js.map +1 -1
  11. package/dist/alignment/SpeechAlignment.d.ts +9 -10
  12. package/dist/alignment/SpeechAlignment.d.ts.map +1 -1
  13. package/dist/alignment/SpeechAlignment.js +23 -96
  14. package/dist/alignment/SpeechAlignment.js.map +1 -1
  15. package/dist/api/APIOptions.d.ts +1 -1
  16. package/dist/api/APIOptions.d.ts.map +1 -1
  17. package/dist/api/Alignment.d.ts +8 -6
  18. package/dist/api/Alignment.d.ts.map +1 -1
  19. package/dist/api/Alignment.js +31 -40
  20. package/dist/api/Alignment.js.map +1 -1
  21. package/dist/api/Common.d.ts +7 -0
  22. package/dist/api/Common.d.ts.map +1 -1
  23. package/dist/api/Common.js.map +1 -1
  24. package/dist/api/Denoising.d.ts +5 -2
  25. package/dist/api/Denoising.d.ts.map +1 -1
  26. package/dist/api/Denoising.js +15 -15
  27. package/dist/api/Denoising.js.map +1 -1
  28. package/dist/api/GlobalOptions.d.ts +2 -8
  29. package/dist/api/GlobalOptions.d.ts.map +1 -1
  30. package/dist/api/GlobalOptions.js +2 -13
  31. package/dist/api/GlobalOptions.js.map +1 -1
  32. package/dist/api/Recognition.d.ts +11 -12
  33. package/dist/api/Recognition.d.ts.map +1 -1
  34. package/dist/api/Recognition.js +32 -82
  35. package/dist/api/Recognition.js.map +1 -1
  36. package/dist/api/SourceSeparation.d.ts +7 -2
  37. package/dist/api/SourceSeparation.d.ts.map +1 -1
  38. package/dist/api/SourceSeparation.js +12 -10
  39. package/dist/api/SourceSeparation.js.map +1 -1
  40. package/dist/api/SpeechLanguageDetection.d.ts +11 -7
  41. package/dist/api/SpeechLanguageDetection.d.ts.map +1 -1
  42. package/dist/api/SpeechLanguageDetection.js +37 -30
  43. package/dist/api/SpeechLanguageDetection.js.map +1 -1
  44. package/dist/api/SpeechSearch.d.ts +4 -1
  45. package/dist/api/SpeechSearch.d.ts.map +1 -1
  46. package/dist/api/SpeechSearch.js.map +1 -1
  47. package/dist/api/SpeechTranslation.d.ts +10 -6
  48. package/dist/api/SpeechTranslation.d.ts.map +1 -1
  49. package/dist/api/SpeechTranslation.js +28 -26
  50. package/dist/api/SpeechTranslation.js.map +1 -1
  51. package/dist/api/Synthesis.d.ts +13 -8
  52. package/dist/api/Synthesis.d.ts.map +1 -1
  53. package/dist/api/Synthesis.js +67 -89
  54. package/dist/api/Synthesis.js.map +1 -1
  55. package/dist/api/TextLanguageDetection.d.ts +4 -2
  56. package/dist/api/TextLanguageDetection.d.ts.map +1 -1
  57. package/dist/api/TextLanguageDetection.js +4 -7
  58. package/dist/api/TextLanguageDetection.js.map +1 -1
  59. package/dist/api/TextTranslation.d.ts +4 -2
  60. package/dist/api/TextTranslation.d.ts.map +1 -1
  61. package/dist/api/TextTranslation.js +13 -24
  62. package/dist/api/TextTranslation.js.map +1 -1
  63. package/dist/api/TimelineTranslationAlignment.d.ts +6 -3
  64. package/dist/api/TimelineTranslationAlignment.d.ts.map +1 -1
  65. package/dist/api/TimelineTranslationAlignment.js +20 -11
  66. package/dist/api/TimelineTranslationAlignment.js.map +1 -1
  67. package/dist/api/TranscriptAndTranslationAlignment.d.ts +6 -4
  68. package/dist/api/TranscriptAndTranslationAlignment.d.ts.map +1 -1
  69. package/dist/api/TranscriptAndTranslationAlignment.js +11 -12
  70. package/dist/api/TranscriptAndTranslationAlignment.js.map +1 -1
  71. package/dist/api/TranslationAlignment.d.ts +6 -4
  72. package/dist/api/TranslationAlignment.d.ts.map +1 -1
  73. package/dist/api/TranslationAlignment.js +20 -21
  74. package/dist/api/TranslationAlignment.js.map +1 -1
  75. package/dist/api/VoiceActivityDetection.d.ts +9 -8
  76. package/dist/api/VoiceActivityDetection.d.ts.map +1 -1
  77. package/dist/api/VoiceActivityDetection.js +20 -43
  78. package/dist/api/VoiceActivityDetection.js.map +1 -1
  79. package/dist/audio/AudioBufferConversion.js.map +1 -1
  80. package/dist/audio/AudioPlayer.d.ts +1 -3
  81. package/dist/audio/AudioPlayer.d.ts.map +1 -1
  82. package/dist/audio/AudioPlayer.js +3 -188
  83. package/dist/audio/AudioPlayer.js.map +1 -1
  84. package/dist/audio/AudioUtilities.d.ts +2 -1
  85. package/dist/audio/AudioUtilities.d.ts.map +1 -1
  86. package/dist/audio/AudioUtilities.js +2 -2
  87. package/dist/audio/AudioUtilities.js.map +1 -1
  88. package/dist/build-tools/MakeTarballsForInstalledPackages.js +3 -3
  89. package/dist/build-tools/MakeTarballsForInstalledPackages.js.map +1 -1
  90. package/dist/cli/CLI.d.ts.map +1 -1
  91. package/dist/cli/CLI.js +139 -92
  92. package/dist/cli/CLI.js.map +1 -1
  93. package/dist/codecs/FFMpegTranscoder.d.ts +4 -3
  94. package/dist/codecs/FFMpegTranscoder.d.ts.map +1 -1
  95. package/dist/codecs/FFMpegTranscoder.js +16 -14
  96. package/dist/codecs/FFMpegTranscoder.js.map +1 -1
  97. package/dist/codecs/TIMITCodec.d.ts.map +1 -1
  98. package/dist/data-structures/DynamicTypedArray.d.ts.map +1 -1
  99. package/dist/data-structures/DynamicTypedArray.js.map +1 -1
  100. package/dist/data-structures/Queue.d.ts.map +1 -1
  101. package/dist/data-structures/WindowedList.d.ts.map +1 -1
  102. package/dist/data-structures/WindowedList.js.map +1 -1
  103. package/dist/denoising/NSNet2.d.ts +3 -2
  104. package/dist/denoising/NSNet2.d.ts.map +1 -1
  105. package/dist/denoising/NSNet2.js +4 -4
  106. package/dist/denoising/NSNet2.js.map +1 -1
  107. package/dist/denoising/RNNoise.d.ts +2 -1
  108. package/dist/denoising/RNNoise.d.ts.map +1 -1
  109. package/dist/denoising/RNNoise.js +10 -6
  110. package/dist/denoising/RNNoise.js.map +1 -1
  111. package/dist/dsp/BiquadFilter.d.ts.map +1 -1
  112. package/dist/dsp/DecayingPeakEstimator.d.ts.map +1 -1
  113. package/dist/dsp/DecayingPeakEstimator.js.map +1 -1
  114. package/dist/dsp/FFT.d.ts.map +1 -1
  115. package/dist/dsp/FFT.js +2 -1
  116. package/dist/dsp/FFT.js.map +1 -1
  117. package/dist/dsp/KWeightingFilter.d.ts.map +1 -1
  118. package/dist/dsp/KWeightingFilter.js.map +1 -1
  119. package/dist/dsp/LoudnessEstimator.d.ts.map +1 -1
  120. package/dist/dsp/LoudnessEstimator.js.map +1 -1
  121. package/dist/dsp/MFCC.d.ts +3 -2
  122. package/dist/dsp/MFCC.d.ts.map +1 -1
  123. package/dist/dsp/MFCC.js +4 -4
  124. package/dist/dsp/MFCC.js.map +1 -1
  125. package/dist/dsp/MelSpectrogram.d.ts +10 -8
  126. package/dist/dsp/MelSpectrogram.d.ts.map +1 -1
  127. package/dist/dsp/MelSpectrogram.js +31 -34
  128. package/dist/dsp/MelSpectrogram.js.map +1 -1
  129. package/dist/dsp/Rubberband.d.ts.map +1 -1
  130. package/dist/dsp/Rubberband.js +8 -3
  131. package/dist/dsp/Rubberband.js.map +1 -1
  132. package/dist/dsp/Sonic.d.ts.map +1 -1
  133. package/dist/dsp/Sonic.js +1 -1
  134. package/dist/dsp/Sonic.js.map +1 -1
  135. package/dist/dsp/SpeexResampler.d.ts.map +1 -1
  136. package/dist/dsp/SpeexResampler.js +1 -1
  137. package/dist/dsp/SpeexResampler.js.map +1 -1
  138. package/dist/encodings/Ascii.d.ts +1 -1
  139. package/dist/encodings/Ascii.d.ts.map +1 -1
  140. package/dist/encodings/Base64.js.map +1 -1
  141. package/dist/encodings/Utf16.d.ts.map +1 -1
  142. package/dist/encodings/Utf8.d.ts.map +1 -1
  143. package/dist/math/VectorMath.d.ts +1 -0
  144. package/dist/math/VectorMath.d.ts.map +1 -1
  145. package/dist/math/VectorMath.js +9 -5
  146. package/dist/math/VectorMath.js.map +1 -1
  147. package/dist/nlp/ChineseSegmentation.d.ts +1 -1
  148. package/dist/nlp/ChineseSegmentation.d.ts.map +1 -1
  149. package/dist/nlp/EspeakPhonemizer.d.ts +1 -1
  150. package/dist/nlp/EspeakPhonemizer.d.ts.map +1 -1
  151. package/dist/nlp/EspeakPhonemizer.js +2 -2
  152. package/dist/nlp/EspeakPhonemizer.js.map +1 -1
  153. package/dist/nlp/IPA.d.ts +2 -2
  154. package/dist/nlp/Segmentation.d.ts +1 -1
  155. package/dist/nlp/Segmentation.d.ts.map +1 -1
  156. package/dist/nlp/Segmentation.js +8 -4
  157. package/dist/nlp/Segmentation.js.map +1 -1
  158. package/dist/recognition/AmazonTranscribeSTT.d.ts +2 -1
  159. package/dist/recognition/AmazonTranscribeSTT.d.ts.map +1 -1
  160. package/dist/recognition/AmazonTranscribeSTT.js +3 -3
  161. package/dist/recognition/AmazonTranscribeSTT.js.map +1 -1
  162. package/dist/recognition/AzureCognitiveServicesSTT.d.ts +2 -1
  163. package/dist/recognition/AzureCognitiveServicesSTT.d.ts.map +1 -1
  164. package/dist/recognition/AzureCognitiveServicesSTT.js +4 -4
  165. package/dist/recognition/AzureCognitiveServicesSTT.js.map +1 -1
  166. package/dist/recognition/DeepgramSTT.d.ts +2 -1
  167. package/dist/recognition/DeepgramSTT.d.ts.map +1 -1
  168. package/dist/recognition/DeepgramSTT.js +12 -13
  169. package/dist/recognition/DeepgramSTT.js.map +1 -1
  170. package/dist/recognition/GoogleCloudSTT.d.ts +2 -1
  171. package/dist/recognition/GoogleCloudSTT.d.ts.map +1 -1
  172. package/dist/recognition/GoogleCloudSTT.js +11 -10
  173. package/dist/recognition/GoogleCloudSTT.js.map +1 -1
  174. package/dist/recognition/OpenAICloudSTT.d.ts +2 -1
  175. package/dist/recognition/OpenAICloudSTT.d.ts.map +1 -1
  176. package/dist/recognition/OpenAICloudSTT.js +6 -6
  177. package/dist/recognition/OpenAICloudSTT.js.map +1 -1
  178. package/dist/recognition/WhisperCommon.d.ts +9 -0
  179. package/dist/recognition/WhisperCommon.d.ts.map +1 -0
  180. package/dist/recognition/WhisperCommon.js +52 -0
  181. package/dist/recognition/WhisperCommon.js.map +1 -0
  182. package/dist/recognition/{WhisperCppSTT.d.ts → WhisperCppCliSTT.d.ts} +14 -16
  183. package/dist/recognition/WhisperCppCliSTT.d.ts.map +1 -0
  184. package/dist/recognition/{WhisperCppSTT.js → WhisperCppCliSTT.js} +55 -84
  185. package/dist/recognition/WhisperCppCliSTT.js.map +1 -0
  186. package/dist/recognition/WhisperSTT.d.ts +72 -85
  187. package/dist/recognition/WhisperSTT.d.ts.map +1 -1
  188. package/dist/recognition/WhisperSTT.js +384 -519
  189. package/dist/recognition/WhisperSTT.js.map +1 -1
  190. package/dist/server/Client.d.ts +2 -2
  191. package/dist/server/Client.d.ts.map +1 -1
  192. package/dist/server/Client.js.map +1 -1
  193. package/dist/server/Worker.d.ts +6 -7
  194. package/dist/server/Worker.d.ts.map +1 -1
  195. package/dist/server/Worker.js +42 -44
  196. package/dist/server/Worker.js.map +1 -1
  197. package/dist/source-separation/MDXNetSourceSeparation.d.ts +3 -2
  198. package/dist/source-separation/MDXNetSourceSeparation.d.ts.map +1 -1
  199. package/dist/source-separation/MDXNetSourceSeparation.js +10 -8
  200. package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
  201. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.d.ts +3 -2
  202. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.d.ts.map +1 -1
  203. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js +5 -5
  204. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js.map +1 -1
  205. package/dist/speech-language-detection/SileroLanguageDetection.d.ts +5 -3
  206. package/dist/speech-language-detection/SileroLanguageDetection.d.ts.map +1 -1
  207. package/dist/speech-language-detection/SileroLanguageDetection.js +8 -8
  208. package/dist/speech-language-detection/SileroLanguageDetection.js.map +1 -1
  209. package/dist/speech-search/DTWSpeechSearch.d.ts +2 -1
  210. package/dist/speech-search/DTWSpeechSearch.d.ts.map +1 -1
  211. package/dist/speech-search/DTWSpeechSearch.js +4 -4
  212. package/dist/speech-search/DTWSpeechSearch.js.map +1 -1
  213. package/dist/subtitles/Subtitles.d.ts +13 -1
  214. package/dist/subtitles/Subtitles.d.ts.map +1 -1
  215. package/dist/subtitles/Subtitles.js +22 -22
  216. package/dist/subtitles/Subtitles.js.map +1 -1
  217. package/dist/synthesis/AwsPollyTTS.d.ts +3 -2
  218. package/dist/synthesis/AwsPollyTTS.d.ts.map +1 -1
  219. package/dist/synthesis/AwsPollyTTS.js +5 -5
  220. package/dist/synthesis/AwsPollyTTS.js.map +1 -1
  221. package/dist/synthesis/AzureCognitiveServicesTTS.d.ts +2 -1
  222. package/dist/synthesis/AzureCognitiveServicesTTS.d.ts.map +1 -1
  223. package/dist/synthesis/AzureCognitiveServicesTTS.js +3 -3
  224. package/dist/synthesis/AzureCognitiveServicesTTS.js.map +1 -1
  225. package/dist/synthesis/CoquiServerTTS.d.ts +2 -1
  226. package/dist/synthesis/CoquiServerTTS.d.ts.map +1 -1
  227. package/dist/synthesis/CoquiServerTTS.js +7 -8
  228. package/dist/synthesis/CoquiServerTTS.js.map +1 -1
  229. package/dist/synthesis/DeepgramTTS.d.ts +2 -2
  230. package/dist/synthesis/DeepgramTTS.d.ts.map +1 -1
  231. package/dist/synthesis/DeepgramTTS.js +13 -13
  232. package/dist/synthesis/DeepgramTTS.js.map +1 -1
  233. package/dist/synthesis/ElevenLabsTTS.d.ts +3 -3
  234. package/dist/synthesis/ElevenLabsTTS.d.ts.map +1 -1
  235. package/dist/synthesis/ElevenLabsTTS.js +22 -21
  236. package/dist/synthesis/ElevenLabsTTS.js.map +1 -1
  237. package/dist/synthesis/EspeakTTS.d.ts +11 -9
  238. package/dist/synthesis/EspeakTTS.d.ts.map +1 -1
  239. package/dist/synthesis/EspeakTTS.js +129 -71
  240. package/dist/synthesis/EspeakTTS.js.map +1 -1
  241. package/dist/synthesis/FliteTTS.d.ts +2 -2
  242. package/dist/synthesis/FliteTTS.d.ts.map +1 -1
  243. package/dist/synthesis/FliteTTS.js +2 -2
  244. package/dist/synthesis/FliteTTS.js.map +1 -1
  245. package/dist/synthesis/GnuSpeechTTS.d.ts +2 -2
  246. package/dist/synthesis/GnuSpeechTTS.d.ts.map +1 -1
  247. package/dist/synthesis/GnuSpeechTTS.js +1 -1
  248. package/dist/synthesis/GnuSpeechTTS.js.map +1 -1
  249. package/dist/synthesis/GoogleCloudTTS.d.ts +3 -2
  250. package/dist/synthesis/GoogleCloudTTS.d.ts.map +1 -1
  251. package/dist/synthesis/GoogleCloudTTS.js +15 -14
  252. package/dist/synthesis/GoogleCloudTTS.js.map +1 -1
  253. package/dist/synthesis/GoogleTranslateTTS.d.ts +3 -2
  254. package/dist/synthesis/GoogleTranslateTTS.d.ts.map +1 -1
  255. package/dist/synthesis/GoogleTranslateTTS.js +11 -11
  256. package/dist/synthesis/GoogleTranslateTTS.js.map +1 -1
  257. package/dist/synthesis/KokoroTTS.d.ts +4 -4
  258. package/dist/synthesis/KokoroTTS.d.ts.map +1 -1
  259. package/dist/synthesis/KokoroTTS.js +11 -10
  260. package/dist/synthesis/KokoroTTS.js.map +1 -1
  261. package/dist/synthesis/MicrosoftEdgeTTS.d.ts +4 -3
  262. package/dist/synthesis/MicrosoftEdgeTTS.d.ts.map +1 -1
  263. package/dist/synthesis/MicrosoftEdgeTTS.js +41 -20
  264. package/dist/synthesis/MicrosoftEdgeTTS.js.map +1 -1
  265. package/dist/synthesis/OpenAICloudTTS.d.ts +2 -2
  266. package/dist/synthesis/OpenAICloudTTS.d.ts.map +1 -1
  267. package/dist/synthesis/OpenAICloudTTS.js +6 -5
  268. package/dist/synthesis/OpenAICloudTTS.js.map +1 -1
  269. package/dist/synthesis/SamTTS.d.ts +2 -1
  270. package/dist/synthesis/SamTTS.d.ts.map +1 -1
  271. package/dist/synthesis/SamTTS.js +2 -2
  272. package/dist/synthesis/SamTTS.js.map +1 -1
  273. package/dist/synthesis/SapiTTS.d.ts +2 -2
  274. package/dist/synthesis/SapiTTS.d.ts.map +1 -1
  275. package/dist/synthesis/SapiTTS.js +2 -2
  276. package/dist/synthesis/SapiTTS.js.map +1 -1
  277. package/dist/synthesis/SvoxPicoTTS.d.ts +2 -2
  278. package/dist/synthesis/SvoxPicoTTS.d.ts.map +1 -1
  279. package/dist/synthesis/SvoxPicoTTS.js +3 -3
  280. package/dist/synthesis/SvoxPicoTTS.js.map +1 -1
  281. package/dist/synthesis/VitsTTS.d.ts +3 -3
  282. package/dist/synthesis/VitsTTS.d.ts.map +1 -1
  283. package/dist/synthesis/VitsTTS.js +14 -11
  284. package/dist/synthesis/VitsTTS.js.map +1 -1
  285. package/dist/text-translation/DeepLTextTranslation.d.ts +2 -2
  286. package/dist/text-translation/DeepLTextTranslation.d.ts.map +1 -1
  287. package/dist/text-translation/DeepLTextTranslation.js +10 -9
  288. package/dist/text-translation/DeepLTextTranslation.js.map +1 -1
  289. package/dist/text-translation/GoogleTranslateTextTranslation.d.ts +4 -4
  290. package/dist/text-translation/GoogleTranslateTextTranslation.d.ts.map +1 -1
  291. package/dist/text-translation/GoogleTranslateTextTranslation.js +20 -18
  292. package/dist/text-translation/GoogleTranslateTextTranslation.js.map +1 -1
  293. package/dist/text-translation/NLLBTextTranslation.d.ts +2 -2
  294. package/dist/text-translation/NLLBTextTranslation.d.ts.map +1 -1
  295. package/dist/text-translation/NLLBTextTranslation.js +4 -6
  296. package/dist/text-translation/NLLBTextTranslation.js.map +1 -1
  297. package/dist/typings/TypedArray.d.ts.map +1 -1
  298. package/dist/utilities/Compression.d.ts +4 -3
  299. package/dist/utilities/Compression.d.ts.map +1 -1
  300. package/dist/utilities/Compression.js +9 -9
  301. package/dist/utilities/Compression.js.map +1 -1
  302. package/dist/utilities/FileDownloader.d.ts +6 -3
  303. package/dist/utilities/FileDownloader.d.ts.map +1 -1
  304. package/dist/utilities/FileDownloader.js +25 -21
  305. package/dist/utilities/FileDownloader.js.map +1 -1
  306. package/dist/utilities/FileReader.d.ts.map +1 -1
  307. package/dist/utilities/FileReader.js.map +1 -1
  308. package/dist/utilities/FileSystem.js +1 -1
  309. package/dist/utilities/FileSystem.js.map +1 -1
  310. package/dist/utilities/FileWriter.d.ts.map +1 -1
  311. package/dist/utilities/FileWriter.js.map +1 -1
  312. package/dist/utilities/Hashing.d.ts.map +1 -1
  313. package/dist/utilities/Hashing.js.map +1 -1
  314. package/dist/utilities/Locale.js.map +1 -1
  315. package/dist/utilities/Logger.d.ts +17 -9
  316. package/dist/utilities/Logger.d.ts.map +1 -1
  317. package/dist/utilities/Logger.js +50 -35
  318. package/dist/utilities/Logger.js.map +1 -1
  319. package/dist/utilities/NpmUtilities.d.ts.map +1 -1
  320. package/dist/utilities/NpmUtilities.js +4 -5
  321. package/dist/utilities/NpmUtilities.js.map +1 -1
  322. package/dist/utilities/ObjectUtilities.js.map +1 -1
  323. package/dist/utilities/OpenPromise.d.ts.map +1 -1
  324. package/dist/utilities/OpenPromise.js.map +1 -1
  325. package/dist/utilities/PackageManager.d.ts +4 -1
  326. package/dist/utilities/PackageManager.d.ts.map +1 -1
  327. package/dist/utilities/PackageManager.js +37 -22
  328. package/dist/utilities/PackageManager.js.map +1 -1
  329. package/dist/utilities/PathUtilities.d.ts +2 -0
  330. package/dist/utilities/PathUtilities.d.ts.map +1 -1
  331. package/dist/utilities/PathUtilities.js +5 -2
  332. package/dist/utilities/PathUtilities.js.map +1 -1
  333. package/dist/utilities/RandomGenerator.d.ts.map +1 -1
  334. package/dist/utilities/SignalChannel.d.ts.map +1 -1
  335. package/dist/utilities/SmoothEstimator.d.ts.map +1 -1
  336. package/dist/utilities/SmoothEstimator.js.map +1 -1
  337. package/dist/utilities/StringBuilder.d.ts.map +1 -1
  338. package/dist/utilities/TarballMaker.d.ts +4 -3
  339. package/dist/utilities/TarballMaker.d.ts.map +1 -1
  340. package/dist/utilities/TarballMaker.js +6 -6
  341. package/dist/utilities/TarballMaker.js.map +1 -1
  342. package/dist/utilities/Timeline.js.map +1 -1
  343. package/dist/utilities/Timer.d.ts.map +1 -1
  344. package/dist/utilities/Utilities.d.ts +9 -7
  345. package/dist/utilities/Utilities.d.ts.map +1 -1
  346. package/dist/utilities/Utilities.js +49 -33
  347. package/dist/utilities/Utilities.js.map +1 -1
  348. package/dist/utilities/VirtualFileReadStream.d.ts.map +1 -1
  349. package/dist/utilities/WebReader.d.ts +2 -1
  350. package/dist/utilities/WebReader.d.ts.map +1 -1
  351. package/dist/utilities/WebReader.js +7 -6
  352. package/dist/utilities/WebReader.js.map +1 -1
  353. package/dist/utilities/WikipediaReader.d.ts +2 -1
  354. package/dist/utilities/WikipediaReader.d.ts.map +1 -1
  355. package/dist/utilities/WikipediaReader.js +1 -5
  356. package/dist/utilities/WikipediaReader.js.map +1 -1
  357. package/dist/voice-activity-detection/AdaptiveGateVAD.d.ts +2 -1
  358. package/dist/voice-activity-detection/AdaptiveGateVAD.d.ts.map +1 -1
  359. package/dist/voice-activity-detection/AdaptiveGateVAD.js +1 -1
  360. package/dist/voice-activity-detection/AdaptiveGateVAD.js.map +1 -1
  361. package/dist/voice-activity-detection/SileroVAD.d.ts.map +1 -1
  362. package/dist/voice-activity-detection/SileroVAD.js.map +1 -1
  363. package/dist/voice-activity-detection/WebRtcVAD.d.ts.map +1 -1
  364. package/dist/voice-activity-detection/WebRtcVAD.js +1 -1
  365. package/dist/voice-activity-detection/WebRtcVAD.js.map +1 -1
  366. package/docs/API.md +62 -17
  367. package/docs/CUDA.md +4 -3
  368. package/docs/Engines.md +8 -7
  369. package/docs/Licenses.md +0 -3
  370. package/docs/Options.md +27 -31
  371. package/docs/Server.md +1 -0
  372. package/docs/Tasklist.md +2 -1
  373. package/docs/Technical.md +2 -2
  374. package/package.json +45 -35
  375. package/src/alignment/SemanticTextAlignment.ts +14 -7
  376. package/src/alignment/SpeechAlignment.ts +47 -150
  377. package/src/api/APIOptions.ts +1 -1
  378. package/src/api/Alignment.ts +95 -61
  379. package/src/api/Common.ts +11 -0
  380. package/src/api/Denoising.ts +22 -17
  381. package/src/api/GlobalOptions.ts +4 -20
  382. package/src/api/Recognition.ts +106 -123
  383. package/src/api/SourceSeparation.ts +39 -21
  384. package/src/api/SpeechLanguageDetection.ts +77 -37
  385. package/src/api/SpeechSearch.ts +6 -1
  386. package/src/api/SpeechTranslation.ts +75 -38
  387. package/src/api/Synthesis.ts +220 -116
  388. package/src/api/TextLanguageDetection.ts +10 -12
  389. package/src/api/TextTranslation.ts +43 -26
  390. package/src/api/TimelineTranslationAlignment.ts +41 -13
  391. package/src/api/TranscriptAndTranslationAlignment.ts +28 -15
  392. package/src/api/TranslationAlignment.ts +53 -24
  393. package/src/api/VoiceActivityDetection.ts +33 -68
  394. package/src/audio/AudioPlayer.ts +4 -232
  395. package/src/audio/AudioUtilities.ts +9 -2
  396. package/src/build-tools/MakeTarballsForInstalledPackages.ts +4 -3
  397. package/src/cli/CLI.ts +209 -101
  398. package/src/codecs/FFMpegTranscoder.ts +18 -16
  399. package/src/denoising/NSNet2.ts +5 -4
  400. package/src/denoising/RNNoise.ts +14 -6
  401. package/src/dsp/FFT.ts +2 -1
  402. package/src/dsp/MFCC.ts +7 -5
  403. package/src/dsp/MelSpectrogram.ts +56 -36
  404. package/src/dsp/Rubberband.ts +10 -3
  405. package/src/dsp/Sonic.ts +2 -1
  406. package/src/dsp/SpeexResampler.ts +2 -1
  407. package/src/encodings/Ascii.ts +1 -1
  408. package/src/encodings/Base64.ts +1 -1
  409. package/src/math/VectorMath.ts +10 -6
  410. package/src/nlp/EspeakPhonemizer.ts +2 -2
  411. package/src/nlp/Segmentation.ts +9 -4
  412. package/src/recognition/AmazonTranscribeSTT.ts +4 -3
  413. package/src/recognition/AzureCognitiveServicesSTT.ts +5 -4
  414. package/src/recognition/DeepgramSTT.ts +16 -16
  415. package/src/recognition/GoogleCloudSTT.ts +14 -12
  416. package/src/recognition/OpenAICloudSTT.ts +12 -6
  417. package/src/recognition/WhisperCommon.ts +130 -0
  418. package/src/recognition/{WhisperCppSTT.ts → WhisperCppCliSTT.ts} +73 -133
  419. package/src/recognition/WhisperSTT.ts +569 -718
  420. package/src/server/Client.ts +4 -4
  421. package/src/server/Worker.ts +59 -59
  422. package/src/source-separation/MDXNetSourceSeparation.ts +15 -10
  423. package/src/speech-embeddings/WavToVec2BertFeatureEmbeddings.ts +7 -5
  424. package/src/speech-language-detection/SileroLanguageDetection.ts +18 -9
  425. package/src/speech-search/DTWSpeechSearch.ts +6 -5
  426. package/src/subtitles/Subtitles.ts +22 -22
  427. package/src/synthesis/AwsPollyTTS.ts +12 -5
  428. package/src/synthesis/AzureCognitiveServicesTTS.ts +11 -3
  429. package/src/synthesis/CoquiServerTTS.ts +8 -7
  430. package/src/synthesis/DeepgramTTS.ts +21 -15
  431. package/src/synthesis/ElevenLabsTTS.ts +33 -23
  432. package/src/synthesis/EspeakTTS.ts +158 -71
  433. package/src/synthesis/FliteTTS.ts +3 -3
  434. package/src/synthesis/GnuSpeechTTS.ts +2 -2
  435. package/src/synthesis/GoogleCloudTTS.ts +23 -14
  436. package/src/synthesis/GoogleTranslateTTS.ts +19 -11
  437. package/src/synthesis/KokoroTTS.ts +37 -14
  438. package/src/synthesis/MicrosoftEdgeTTS.ts +71 -23
  439. package/src/synthesis/OpenAICloudTTS.ts +7 -6
  440. package/src/synthesis/SamTTS.ts +3 -2
  441. package/src/synthesis/SapiTTS.ts +3 -3
  442. package/src/synthesis/SvoxPicoTTS.ts +4 -5
  443. package/src/synthesis/VitsTTS.ts +43 -15
  444. package/src/text-translation/DeepLTextTranslation.ts +12 -10
  445. package/src/text-translation/GoogleTranslateTextTranslation.ts +35 -21
  446. package/src/text-translation/NLLBTextTranslation.ts +5 -9
  447. package/src/typings/Fillers.d.ts +0 -60
  448. package/src/typings/TypedArray.ts +12 -1
  449. package/src/utilities/Compression.ts +10 -9
  450. package/src/utilities/FileDownloader.ts +38 -23
  451. package/src/utilities/FileSystem.ts +1 -1
  452. package/src/utilities/Logger.ts +59 -42
  453. package/src/utilities/NpmUtilities.ts +4 -6
  454. package/src/utilities/OpenPromise.ts +1 -2
  455. package/src/utilities/PackageManager.ts +53 -26
  456. package/src/utilities/PathUtilities.ts +6 -2
  457. package/src/utilities/TarballMaker.ts +7 -7
  458. package/src/utilities/Utilities.ts +58 -41
  459. package/src/utilities/WebReader.ts +11 -6
  460. package/src/utilities/WikipediaReader.ts +2 -8
  461. package/src/voice-activity-detection/AdaptiveGateVAD.ts +13 -8
  462. package/src/voice-activity-detection/WebRtcVAD.ts +1 -2
  463. package/tsconfig.json +53 -54
  464. package/dist/audio/AudioRecorder.d.ts +0 -4
  465. package/dist/audio/AudioRecorder.d.ts.map +0 -1
  466. package/dist/audio/AudioRecorder.js +0 -68
  467. package/dist/audio/AudioRecorder.js.map +0 -1
  468. package/dist/audio/SoxPath.d.ts +0 -2
  469. package/dist/audio/SoxPath.d.ts.map +0 -1
  470. package/dist/audio/SoxPath.js +0 -24
  471. package/dist/audio/SoxPath.js.map +0 -1
  472. package/dist/recognition/SileroSTT.d.ts +0 -30
  473. package/dist/recognition/SileroSTT.d.ts.map +0 -1
  474. package/dist/recognition/SileroSTT.js +0 -152
  475. package/dist/recognition/SileroSTT.js.map +0 -1
  476. package/dist/recognition/VoskSTT.d.ts +0 -11
  477. package/dist/recognition/VoskSTT.d.ts.map +0 -1
  478. package/dist/recognition/VoskSTT.js +0 -72
  479. package/dist/recognition/VoskSTT.js.map +0 -1
  480. package/dist/recognition/WhisperCppSTT.d.ts.map +0 -1
  481. package/dist/recognition/WhisperCppSTT.js.map +0 -1
  482. package/dist/synthesis/StreamlabsPollyTTS.d.ts +0 -12
  483. package/dist/synthesis/StreamlabsPollyTTS.d.ts.map +0 -1
  484. package/dist/synthesis/StreamlabsPollyTTS.js +0 -88
  485. package/dist/synthesis/StreamlabsPollyTTS.js.map +0 -1
  486. package/dist/utilities/BinaryUtilities.d.ts +0 -14
  487. package/dist/utilities/BinaryUtilities.d.ts.map +0 -1
  488. package/dist/utilities/BinaryUtilities.js +0 -113
  489. package/dist/utilities/BinaryUtilities.js.map +0 -1
  490. package/src/audio/AudioRecorder.ts +0 -91
  491. package/src/audio/SoxPath.ts +0 -31
  492. package/src/recognition/SileroSTT.ts +0 -222
  493. package/src/recognition/VoskSTT.ts +0 -112
  494. package/src/synthesis/StreamlabsPollyTTS.ts +0 -114
  495. package/src/utilities/BinaryUtilities.ts +0 -138
  496. /package/{LICENSE.md → LICENSE.GPLv3.md} +0 -0
@@ -1,40 +1,45 @@
1
1
  import chalk from 'chalk'
2
- import type * as Onnx from 'onnxruntime-node'
3
2
 
3
+ import { getRawAudioDuration, RawAudio } from '../audio/AudioUtilities.js'
4
+ import { formatLanguageCodeWithName, getShortLanguageCode, languageCodeToName } from '../utilities/Locale.js'
4
5
  import { Logger } from '../utilities/Logger.js'
5
- import { computeMelSpectrogramUsingFilterbanks, Filterbank } from '../dsp/MelSpectrogram.js'
6
- import { clip, getIntegerRange, getTopKIndexes, splitFloat32Array, yieldToEventLoop } from '../utilities/Utilities.js'
7
- import { indexOfMax, logOfVector, logSumExp, meanOfVector, medianOfVector, softmax, sumAndSumOfSquaresOfVector, sumOfSquaresOfVector, sumVector } from '../math/VectorMath.js'
8
-
9
- import { alignDTWWindowed } from '../alignment/DTWSequenceAlignmentWindowed.js'
10
6
  import { extendDeep } from '../utilities/ObjectUtilities.js'
11
7
  import { Timeline, TimelineEntry } from '../utilities/Timeline.js'
12
- import { AlignmentPath } from '../alignment/SpeechAlignment.js'
13
- import { getRawAudioDuration, RawAudio, sliceRawAudio } from '../audio/AudioUtilities.js'
14
- import { readFileAsUtf8 } from '../utilities/FileSystem.js'
15
- import { logLevelGreaterOrEqualTo, type LanguageDetectionResults } from '../api/API.js'
16
- import { formatLanguageCodeWithName, getShortLanguageCode, languageCodeToName } from '../utilities/Locale.js'
17
- import { loadPackage } from '../utilities/PackageManager.js'
18
- import { XorShift32PRNG } from '../utilities/RandomGenerator.js'
19
- import { detectSpeechLanguageByParts } from '../api/SpeechLanguageDetection.js'
8
+
20
9
  import { type Tiktoken } from 'tiktoken/lite'
21
- import { includesPunctuation, isWhitespace, splitToWords } from '../nlp/Segmentation.js'
22
- import { medianOf5Filter } from '../math/MedianFilter.js'
23
- import { getDeflateCompressionMetricsForString } from '../utilities/Compression.js'
24
- import { dmlProviderAvailable, getOnnxSessionOptions, makeOnnxLikeFloat32Tensor, OnnxExecutionProvider, OnnxLikeFloat32Tensor } from '../utilities/OnnxUtilities.js'
10
+ import { XorShift32PRNG } from '../utilities/RandomGenerator.js'
25
11
  import { murmurHash3_int32Input } from '../utilities/Hashing.js'
12
+
13
+ import { WhisperCppContext, WhisperCppContextOptions, CrossAttentionQKs, WhisperAlignmentHeadsPreset, whisperModelIdToTextLayerCount, whisperModelIdToAlignmentHeadsPreset, GgmlLogLevel } from '@echogarden/whisper.cpp-binding'
14
+ import { getDeflateCompressionMetricsForString } from '../utilities/Compression.js'
15
+ import { indexOfMax, meanOfVector, medianOfVector, softmax, sumAndSumOfSquaresOfVector, sumVector } from '../math/VectorMath.js'
16
+ import { loadPackage, LoadPackageCallbacks } from '../utilities/PackageManager.js'
17
+ import { joinPath, resolvePath } from '../utilities/PathUtilities.js'
18
+ import { readFileAsUtf8 } from '../utilities/FileSystem.js'
19
+ import { clip, concatUint8Arrays, getIntegerRange, getTopKIndexes, yieldToEventLoop } from '../utilities/Utilities.js'
26
20
  import { containsInvalidCodepoint, getTokenRepetitionScore } from '../utilities/StringUtilities.js'
27
- import { joinPath } from '../utilities/PathUtilities.js'
21
+ import { alignDTWWindowed } from '../alignment/DTWSequenceAlignmentWindowed.js'
22
+ import { medianOf5Filter } from '../math/MedianFilter.js'
23
+ import { AlignmentPath } from '../alignment/SpeechAlignment.js'
24
+ import { includesPunctuation, isAllWhitespace, splitToWords } from '../nlp/Segmentation.js'
28
25
  import { Timer } from '../utilities/Timer.js'
26
+ import { computeMelSpectrogramUsingFilterbank, Filterbank } from '../dsp/MelSpectrogram.js'
27
+ import { detectSpeechLanguageByParts, SpeechLanguageDetectionCallbacks } from '../api/SpeechLanguageDetection.js'
28
+ import { LanguageDetectionResults } from '../api/LanguageDetectionCommon.js'
29
+ import { RecognitionCallbacks } from '../api/Recognition.js'
30
+ import { AlignmentCallbacks } from '../api/Alignment.js'
31
+ import { OperationCallbacks } from '../api/Common.js'
32
+ import { WhisperModelId, WhisperQuantizedModelId, WhisperTask } from './WhisperCommon.js'
29
33
 
30
34
  export async function recognize(
31
35
  sourceRawAudio: RawAudio,
32
- modelName: WhisperModelName,
33
- modelDir: string,
36
+ modelId: WhisperModelId,
37
+ modelPath: string,
38
+ libPath: string,
34
39
  task: WhisperTask,
35
40
  sourceLanguage: string,
36
41
  options: WhisperOptions,
37
- onPart?: WhisperPartCallback) {
42
+ callbacks: RecognitionCallbacks) {
38
43
 
39
44
  options = extendDeep(defaultWhisperOptions, options)
40
45
 
@@ -48,11 +53,11 @@ export async function recognize(
48
53
  throw new Error(`The language ${formatLanguageCodeWithName(sourceLanguage)} is not supported by the Whisper engine.`)
49
54
  }
50
55
 
51
- if (isEnglishOnlyModel(modelName) && sourceLanguage !== 'en') {
52
- throw new Error(`The model '${modelName}' can only be used with English inputs. However, the given source language was ${languageCodeToName(sourceLanguage)}.`)
56
+ if (isEnglishOnlyModel(modelId) && sourceLanguage !== 'en') {
57
+ throw new Error(`The model '${modelId}' can only be used with English inputs. However, the given source language was ${languageCodeToName(sourceLanguage)}.`)
53
58
  }
54
59
 
55
- if (modelName === 'large-v3-turbo' && task === 'translate') {
60
+ if (modelId === 'large-v3-turbo' && task === 'translate') {
56
61
  throw new Error(`The 'large-v3-turbo' model doesn't support translation tasks.`)
57
62
  }
58
63
 
@@ -62,28 +67,26 @@ export async function recognize(
62
67
 
63
68
  // Workaround issue with large-v3-turbo that produces invalid results when a prompt is passed to it.
64
69
  // Always disable autoprompting for that model.
65
- if (options.autoPromptParts && modelName === 'large-v3-turbo') {
70
+ if (options.autoPromptParts && modelId === 'large-v3-turbo') {
66
71
  options.autoPromptParts = false
67
72
  }
68
73
 
69
- // Select encoder ONNX provider
70
- const encoderProviders: OnnxExecutionProvider[] =
71
- options.encoderProvider ? [options.encoderProvider] : getDefaultEncoderProvidersForModel(modelName)
72
-
73
- // Select decoder ONNX provider
74
- const decoderProviders: OnnxExecutionProvider[] =
75
- options.decoderProvider ? [options.decoderProvider] : getDefaultDecoderProvidersForModel(modelName)
76
-
77
74
  const seed = options.seed
78
75
 
79
76
  const whisper = new Whisper(
80
- modelName,
81
- modelDir,
82
- encoderProviders,
83
- decoderProviders,
77
+ modelId,
78
+ modelPath,
79
+ libPath,
84
80
  seed)
85
81
 
86
- const result = await whisper.recognize(sourceRawAudio, task, sourceLanguage, options, undefined, onPart)
82
+ const result = await whisper.recognize(
83
+ sourceRawAudio,
84
+ task,
85
+ sourceLanguage,
86
+ options,
87
+ undefined,
88
+ callbacks,
89
+ )
87
90
 
88
91
  return result
89
92
  }
@@ -91,10 +94,12 @@ export async function recognize(
91
94
  export async function align(
92
95
  sourceRawAudio: RawAudio,
93
96
  transcript: string,
94
- modelName: WhisperModelName,
95
- modelDir: string,
97
+ modelId: WhisperModelId,
98
+ modelPath: string,
99
+ libPath: string,
96
100
  sourceLanguage: string,
97
- options: WhisperAlignmentOptions) {
101
+ options: WhisperAlignmentOptions,
102
+ callbacks: RecognitionCallbacks) {
98
103
 
99
104
  options = extendDeep(defaultWhisperAlignmentOptions, options)
100
105
 
@@ -108,25 +113,24 @@ export async function align(
108
113
  throw new Error(`The language ${formatLanguageCodeWithName(sourceLanguage)} is not supported by the Whisper engine.`)
109
114
  }
110
115
 
111
- if (isEnglishOnlyModel(modelName) && sourceLanguage !== 'en') {
112
- throw new Error(`The model '${modelName}' can only be used with English inputs. However, the given source language was ${languageCodeToName(sourceLanguage)}.`)
116
+ if (isEnglishOnlyModel(modelId) && sourceLanguage !== 'en') {
117
+ throw new Error(`The model '${modelId}' can only be used with English inputs. However, the given source language was ${languageCodeToName(sourceLanguage)}.`)
113
118
  }
114
119
 
115
- // Select encoder ONNX provider
116
- const encoderProviders: OnnxExecutionProvider[] =
117
- options.encoderProvider ? [options.encoderProvider] : getDefaultEncoderProvidersForModel(modelName)
118
-
119
- // Select decoder ONNX provider
120
- const decoderProviders: OnnxExecutionProvider[] =
121
- options.decoderProvider ? [options.decoderProvider] : getDefaultDecoderProvidersForModel(modelName)
122
-
123
120
  const whisper = new Whisper(
124
- modelName,
125
- modelDir,
126
- encoderProviders,
127
- decoderProviders,)
128
-
129
- const timeline = await whisper.align(sourceRawAudio, transcript, sourceLanguage, 'transcribe', options)
121
+ modelId,
122
+ modelPath,
123
+ libPath,
124
+ )
125
+
126
+ const timeline = await whisper.align(
127
+ sourceRawAudio,
128
+ transcript,
129
+ sourceLanguage,
130
+ 'transcribe',
131
+ options,
132
+ callbacks,
133
+ )
130
134
 
131
135
  return timeline
132
136
  }
@@ -134,10 +138,12 @@ export async function align(
134
138
  export async function alignEnglishTranslation(
135
139
  sourceRawAudio: RawAudio,
136
140
  translatedTranscript: string,
137
- modelName: WhisperModelName,
138
- modelDir: string,
141
+ modelId: WhisperModelId,
142
+ modelPath: string,
143
+ libPath: string,
139
144
  sourceLanguage: string,
140
- options: WhisperAlignmentOptions) {
145
+ options: WhisperAlignmentOptions,
146
+ callbacks: AlignmentCallbacks) {
141
147
 
142
148
  options = extendDeep(defaultWhisperAlignmentOptions, options)
143
149
 
@@ -151,38 +157,39 @@ export async function alignEnglishTranslation(
151
157
  throw new Error(`The source language ${formatLanguageCodeWithName(sourceLanguage)} is not supported by the Whisper engine.`)
152
158
  }
153
159
 
154
- if (modelName === 'large-v3-turbo') {
160
+ if (modelId === 'large-v3-turbo') {
155
161
  throw new Error(`The 'large-v3-turbo' model doesn't support translation tasks, so cannot be used for translation alignment.`)
156
162
  }
157
163
 
158
- if (isEnglishOnlyModel(modelName)) {
164
+ if (isEnglishOnlyModel(modelId)) {
159
165
  throw new Error(`Translation alignment can only be done with multilingual models.`)
160
166
  }
161
167
 
162
- // Select encoder ONNX provider
163
- const encoderProviders: OnnxExecutionProvider[] =
164
- options.encoderProvider ? [options.encoderProvider] : getDefaultEncoderProvidersForModel(modelName)
165
-
166
- // Select decoder ONNX provider
167
- const decoderProviders: OnnxExecutionProvider[] =
168
- options.decoderProvider ? [options.decoderProvider] : getDefaultDecoderProvidersForModel(modelName)
169
-
170
168
  const whisper = new Whisper(
171
- modelName,
172
- modelDir,
173
- encoderProviders,
174
- decoderProviders,)
175
-
176
- const timeline = await whisper.align(sourceRawAudio, translatedTranscript, sourceLanguage, 'translate', options)
169
+ modelId,
170
+ modelPath,
171
+ libPath
172
+ )
173
+
174
+ const timeline = await whisper.align(
175
+ sourceRawAudio,
176
+ translatedTranscript,
177
+ sourceLanguage,
178
+ 'translate',
179
+ options,
180
+ callbacks,
181
+ )
177
182
 
178
183
  return timeline
179
184
  }
180
185
 
181
186
  export async function detectLanguage(
182
187
  sourceRawAudio: RawAudio,
183
- modelName: WhisperModelName,
184
- modelDir: string,
185
- options: WhisperLanguageDetectionOptions) {
188
+ modelId: WhisperModelId,
189
+ modelPath: string,
190
+ libPath: string,
191
+ options: WhisperLanguageDetectionOptions,
192
+ callbacks: SpeechLanguageDetectionCallbacks) {
186
193
 
187
194
  options = extendDeep(defaultWhisperLanguageDetectionOptions, options)
188
195
 
@@ -190,7 +197,7 @@ export async function detectLanguage(
190
197
  throw new Error('Source audio must have a sample rate of 16000 Hz')
191
198
  }
192
199
 
193
- if (!isMultilingualModel(modelName)) {
200
+ if (!isMultilingualModel(modelId)) {
194
201
  throw new Error(`Language detection is only supported with multilingual models.`)
195
202
  }
196
203
 
@@ -198,165 +205,50 @@ export async function detectLanguage(
198
205
  throw new Error(`Temperature cannot be negative`)
199
206
  }
200
207
 
201
- // Select encoder ONNX provider
202
- const encoderProviders: OnnxExecutionProvider[] =
203
- options.encoderProvider ? [options.encoderProvider] : getDefaultEncoderProvidersForModel(modelName)
204
-
205
- // Select decoder ONNX provider
206
- const decoderProviders: OnnxExecutionProvider[] =
207
- options.decoderProvider ? [options.decoderProvider] : []
208
-
209
208
  const whisper = new Whisper(
210
- modelName,
211
- modelDir,
212
- encoderProviders,
213
- decoderProviders)
209
+ modelId,
210
+ modelPath,
211
+ libPath,
212
+ )
214
213
 
215
214
  async function detectLanguageForPart(partAudio: RawAudio) {
216
- const audioFeatures = await whisper.encodeAudio(partAudio)
217
- const partResults = await whisper.detectLanguage(audioFeatures, options.temperature!)
215
+ const partResults = await whisper.detectLanguage(partAudio, options, callbacks)
218
216
 
219
217
  return partResults
220
218
  }
221
219
 
222
- const results = await detectSpeechLanguageByParts(sourceRawAudio, detectLanguageForPart)
220
+ const results = await detectSpeechLanguageByParts(
221
+ sourceRawAudio,
222
+ detectLanguageForPart,
223
+ undefined,
224
+ undefined,
225
+ callbacks,
226
+ )
223
227
 
224
228
  results.sort((entry1, entry2) => entry2.probability - entry1.probability)
225
229
 
226
230
  return results
227
231
  }
228
232
 
229
- export async function detectVoiceActivity(
230
- sourceRawAudio: RawAudio,
231
- modelName: WhisperModelName,
232
- modelDir: string,
233
- options: WhisperVADOptions) {
234
-
235
- options = extendDeep(defaultWhisperVADOptions, options)
236
-
237
- if (sourceRawAudio.sampleRate !== 16000) {
238
- throw new Error('Source audio must have a sample rate of 16000 Hz')
239
- }
240
-
241
- if (options.temperature! < 0) {
242
- throw new Error(`Temperature cannot be negative`)
243
- }
244
-
245
- const audioSamples = sourceRawAudio.audioChannels[0]
246
-
247
- const partDuration = 5
248
- const maxSamplesCountForPart = sourceRawAudio.sampleRate * partDuration
249
-
250
- // Select encoder ONNX provider
251
- const encoderProviders: OnnxExecutionProvider[] =
252
- options.encoderProvider ? [options.encoderProvider] : getDefaultEncoderProvidersForModel(modelName)
253
-
254
- // Select decoder ONNX provider
255
- const decoderProviders: OnnxExecutionProvider[] =
256
- options.decoderProvider ? [options.decoderProvider] : []
257
-
258
- const whisper = new Whisper(
259
- modelName,
260
- modelDir,
261
- encoderProviders,
262
- decoderProviders)
263
-
264
- const partProbabilities: Timeline = []
265
-
266
- for (let sampleOffset = 0; sampleOffset < audioSamples.length; sampleOffset += maxSamplesCountForPart) {
267
- const partSamples = sliceRawAudio(sourceRawAudio, sampleOffset, sampleOffset + maxSamplesCountForPart)
268
-
269
- const samplesCountForPart = partSamples.audioChannels[0].length
270
-
271
- const startTime = sampleOffset / sourceRawAudio.sampleRate
272
- const endTime = (sampleOffset + samplesCountForPart) / sourceRawAudio.sampleRate
273
-
274
- const encodedPartSamples = await whisper.encodeAudio(partSamples)
275
- const probabilityForPart = await whisper.detectVoiceActivity(encodedPartSamples, options.temperature!)
276
-
277
- partProbabilities.push({
278
- type: 'segment',
279
- text: '',
280
- startTime,
281
- endTime,
282
- confidence: probabilityForPart,
283
- })
284
- }
285
-
286
- return { partProbabilities }
287
- }
288
-
289
233
  export class Whisper {
290
234
  isMultiligualModel: boolean
291
235
 
292
- audioEncoder?: Onnx.InferenceSession
293
- textDecoder?: Onnx.InferenceSession
294
-
295
236
  tiktoken?: Tiktoken
237
+ context?: WhisperCppContext
296
238
 
297
- tokenConfig: {
298
- endOfTextToken: number
299
- startOfTextToken: number
300
-
301
- languageTokensStart: number
302
- languageTokensEnd: number
303
-
304
- translateTaskToken: number
305
- transcribeTaskToken: number
306
- startOfPromptToken: number
307
- nonSpeechToken: number
308
- noTimestampsToken: number
309
-
310
- timestampTokensStart: number
311
- timestampTokensEnd: number
312
- }
239
+ specialTokens: WhisperSpecialTokens
313
240
 
314
241
  randomGen: XorShift32PRNG
315
242
 
316
243
  constructor(
317
- public readonly modelName: WhisperModelName,
318
- public readonly modelDir: string,
319
- public readonly encoderExecutionProviders: OnnxExecutionProvider[],
320
- public readonly decoderExecutionProviders: OnnxExecutionProvider[],
244
+ public readonly modelId: WhisperModelId,
245
+ public readonly modelPath: string,
246
+ public readonly libPath: string,
321
247
  prngSeed = 1234) {
322
248
 
323
- this.isMultiligualModel = isMultilingualModel(this.modelName)
249
+ this.isMultiligualModel = isMultilingualModel(this.modelId)
324
250
 
325
- if (this.isMultiligualModel) {
326
- this.tokenConfig = {
327
- endOfTextToken: 50257,
328
- startOfTextToken: 50258,
329
-
330
- languageTokensStart: 50259,
331
- languageTokensEnd: 50358,
332
-
333
- translateTaskToken: 50358,
334
- transcribeTaskToken: 50359,
335
- startOfPromptToken: 50361,
336
- nonSpeechToken: 50362,
337
- noTimestampsToken: 50363,
338
-
339
- timestampTokensStart: 50364,
340
- timestampTokensEnd: 50364 + 1501,
341
- }
342
- } else {
343
- this.tokenConfig = {
344
- endOfTextToken: 50256,
345
- startOfTextToken: 50257,
346
-
347
- languageTokensStart: 50258,
348
- languageTokensEnd: 50358,
349
-
350
- translateTaskToken: 50358,
351
- transcribeTaskToken: 50359,
352
- startOfPromptToken: 50360,
353
- nonSpeechToken: 50361,
354
- noTimestampsToken: 50362,
355
-
356
- timestampTokensStart: 50363,
357
- timestampTokensEnd: 50363 + 1501,
358
- }
359
- }
251
+ this.specialTokens = this.isMultiligualModel ? multilingualSpecialTokens : englishOnlySpecialTokens
360
252
 
361
253
  this.randomGen = new XorShift32PRNG(murmurHash3_int32Input(prngSeed))
362
254
  }
@@ -366,15 +258,15 @@ export class Whisper {
366
258
  task: WhisperTask,
367
259
  language: string,
368
260
  options: WhisperOptions,
369
- logitFilter?: WhisperLogitFilter,
370
- onPart?: WhisperPartCallback,
371
- ) {
372
- await this.initializeIfNeeded()
261
+ logitFilter: WhisperLogitFilter | undefined,
262
+ callbacks: RecognitionCallbacks) {
263
+
264
+ await this.initializeIfNeeded(options, callbacks)
373
265
 
374
- const logger = new Logger()
266
+ const logger = new Logger(callbacks.logLevel)
375
267
 
376
268
  options = extendDeep(defaultWhisperOptions, options)
377
- options.model = this.modelName
269
+ options.model = this.modelId
378
270
 
379
271
  if (!options.timestampAccuracy) {
380
272
  options.timestampAccuracy = this.defaultTimestampAccuracy
@@ -406,17 +298,21 @@ export class Whisper {
406
298
  }
407
299
 
408
300
  for (let audioOffset = 0; audioOffset < audioSamples.length;) {
301
+ callbacks?.abortSignal?.throwIfAborted()
302
+
409
303
  const segmentStartTime = audioOffset / sampleRate
410
304
 
411
305
  await logger.startAsync(`\nPrepare audio part at time position ${segmentStartTime.toFixed(2)}`, undefined, chalk.magentaBright)
412
306
 
413
- const audioPartSamples = audioSamples.slice(audioOffset, audioOffset + maxAudioSamplesPerPart)
307
+ const audioPartSamples = audioSamples.subarray(audioOffset, audioOffset + maxAudioSamplesPerPart)
414
308
  const audioPartRawAudio: RawAudio = { audioChannels: [audioPartSamples], sampleRate }
415
309
  const audioPartDuration = getRawAudioDuration(audioPartRawAudio)
416
310
 
417
311
  logger.end()
418
312
 
419
- const audioPartFeatures = await this.encodeAudio(audioPartRawAudio)
313
+ await this.encodeSamples(audioPartSamples, false, options, callbacks)
314
+
315
+ await logger.startAsync(`Prepare prompt`)
420
316
 
421
317
  const isFirstPart = audioOffset === 0
422
318
  const isFinalPart = audioOffset + maxAudioSamplesPerPart >= audioSamples.length
@@ -430,9 +326,9 @@ export class Whisper {
430
326
  promptTokens = promptTokens.slice(promptTokens.length - largestMaximumTokensPerPart)
431
327
  }
432
328
 
433
- initialTokens = [this.tokenConfig.startOfPromptToken, ...promptTokens]
329
+ initialTokens = [this.specialTokens.startOfPromptToken, ...promptTokens]
434
330
  } else if (options.autoPromptParts && previousPartTextTokens.length > 0) {
435
- initialTokens = [this.tokenConfig.startOfPromptToken, ...previousPartTextTokens]
331
+ initialTokens = [this.specialTokens.startOfPromptToken, ...previousPartTextTokens]
436
332
  }
437
333
 
438
334
  initialTokens = [...initialTokens, ...this.getTextStartTokens(language, task, !decodeTimestampTokens)]
@@ -450,13 +346,13 @@ export class Whisper {
450
346
  decodedTokensInferenceTime: partTokensInferenceTime,
451
347
  decodedTokensOverheadTime: partTokensOverheadTime,
452
348
  } = await this.decodeTokens(
453
- audioPartFeatures,
454
349
  initialTokens,
455
350
  audioPartDuration,
456
351
  isFirstPart,
457
352
  isFinalPart,
458
353
  options,
459
354
  wrappedLogitFilter,
355
+ callbacks,
460
356
  )
461
357
 
462
358
  const lastToken = partTokens[partTokens.length - 1]
@@ -477,36 +373,23 @@ export class Whisper {
477
373
 
478
374
  await logger.startAsync(`Extract timeline for part (timestamp accuracy: ${options.timestampAccuracy!})`)
479
375
 
480
- if (partTokens.length !== partTokensCrossAttentionQKs.length) {
481
- throw new Error('Unexpected: partTokens.length !== partCrossAttentionQKs.length')
482
- }
483
-
484
376
  // Prepare tokens
485
377
  partTokens = partTokens.slice(initialTokens.length)
486
378
  partTokensConfidence = partTokensConfidence.slice(initialTokens.length)
487
379
  partTokensCrossAttentionQKs = partTokensCrossAttentionQKs.slice(initialTokens.length)
488
380
 
489
- // Find alignment path
490
- let alignmentHeads: number[] | undefined
491
-
492
- if (options.timestampAccuracy === 'medium' || options.model === 'large-v3-turbo') {
493
- alignmentHeads = this.alignmentHeadIndexes
494
- } else if (options.timestampAccuracy === 'high') {
495
- alignmentHeads = undefined
496
- } else {
497
- throw new Error(`Unsupported timestamp accuracy '${options.timestampAccuracy}', can only be 'medium' or 'high'.`)
498
- }
499
-
500
- const alignmentPath = await this.findAlignmentPathFromQKs(partTokensCrossAttentionQKs, partTokens, 0, segmentFrameCount, alignmentHeads)
381
+ // Find alignment path using the cross attention query-key tensors
382
+ const alignmentHeadsIndexesToUse = undefined
383
+ const alignmentPath = await this.findAlignmentPathFromCrossAttentionQKs(partTokensCrossAttentionQKs, partTokens, 0, segmentFrameCount, alignmentHeadsIndexesToUse)
501
384
 
502
385
  // Generate timeline from alignment path
503
386
  const partTimeline = await this.getTokenTimelineFromAlignmentPath(alignmentPath, partTokens, segmentStartTime, segmentEndTime, partTokensConfidence)
504
387
 
505
- if (onPart) {
388
+ if (callbacks?.onPart) {
506
389
  const partWordTimeline = this.tokenTimelineToWordTimeline(partTimeline, language)
507
390
  const partTranscript = this.tokensToText(partTokens)
508
391
 
509
- onPart(partTranscript, partTimeline, partWordTimeline)
392
+ callbacks.onPart(partTranscript, partTimeline, partWordTimeline)
510
393
  }
511
394
 
512
395
  // Add tokens to output
@@ -529,13 +412,13 @@ export class Whisper {
529
412
 
530
413
  logger.end()
531
414
 
532
- if (logLevelGreaterOrEqualTo('trace')) {
533
- const promptDecodingTime = partTokensDecodingTime[0]
415
+ if (logger.logLevelGreaterOrEqualTo('trace')) {
416
+ const promptProcessingTime = partTokensDecodingTime[0]
534
417
  const medianTokenDecodingTime = medianOfVector(partTokensDecodingTime.slice(1))
535
418
  const medianTokenInferenceTime = medianOfVector(partTokensInferenceTime.slice(1))
536
419
  const medianOverheadTime = medianOfVector(partTokensOverheadTime.slice(1))
537
420
 
538
- logger.log(`${chalk.blueBright('Context')}: ${initialTokens.length + partTokens.length} tokens (${initialTokens.length} prompt, ${partTokens.length} decoded)\n${chalk.blueBright('Prompt decode time')}: ${promptDecodingTime.toFixed(1)}ms\n${chalk.blueBright('Median token decode time')}: ${medianTokenDecodingTime.toFixed(1)}ms (${medianTokenInferenceTime.toFixed(1)}ms inference, ${medianOverheadTime.toFixed(2)}ms overhead)`, 'trace')
421
+ logger.log(`${chalk.blueBright('Context')}: ${initialTokens.length + partTokens.length} tokens (${initialTokens.length} prompt, ${partTokens.length} decoded)\n${chalk.blueBright('Prompt processing time')}: ${promptProcessingTime.toFixed(1)}ms\n${chalk.blueBright('Median token decode time')}: ${medianTokenDecodingTime.toFixed(1)}ms (${medianTokenInferenceTime.toFixed(1)}ms inference, ${medianOverheadTime.toFixed(2)}ms overhead)`, 'trace')
539
422
  }
540
423
  }
541
424
 
@@ -550,8 +433,12 @@ export class Whisper {
550
433
  return { transcript, timeline, allDecodedTokens }
551
434
  }
552
435
 
553
- async align(rawAudio: RawAudio, transcript: string, sourceLanguage: string, task: 'transcribe' | 'translate', whisperAlignmentOptions: WhisperAlignmentOptions) {
554
- await this.initializeTokenizerIfNeeded()
436
+ async align(rawAudio: RawAudio, transcript: string, sourceLanguage: string, task: 'transcribe' | 'translate', whisperAlignmentOptions: WhisperAlignmentOptions, callbacks: RecognitionCallbacks) {
437
+ await this.initializeTokenizerIfNeeded(callbacks)
438
+
439
+ const logger = new Logger(callbacks.logLevel)
440
+
441
+ logger.start(`Tokenize transcript`)
555
442
 
556
443
  whisperAlignmentOptions = extendDeep(defaultWhisperAlignmentOptions, whisperAlignmentOptions)
557
444
 
@@ -580,7 +467,7 @@ export class Whisper {
580
467
 
581
468
  // Initialize custom logit filter that allows only the transcript tokens to be decoded
582
469
  // in order.
583
- const endOfTextToken = this.tokenConfig.endOfTextToken
470
+ const endOfTextToken = this.specialTokens.endOfTextToken
584
471
 
585
472
  const logitFilter: WhisperLogitFilter = (logits, decodedTokens, isFirstPart, isFinalPart) => {
586
473
  const decodedTextTokens = decodedTokens.filter(token => this.isTextToken(token))
@@ -607,8 +494,14 @@ export class Whisper {
607
494
 
608
495
  // Set options for alignment
609
496
  const options: WhisperOptions = {
610
- model: this.modelName,
497
+ model: this.modelId,
498
+
499
+ enableGPU: whisperAlignmentOptions.enableGPU,
500
+ threadCount: whisperAlignmentOptions.threadCount,
501
+ gpuDeviceIndex: whisperAlignmentOptions.gpuDeviceIndex,
502
+
611
503
  temperature: 0.0,
504
+
612
505
  prompt: undefined,
613
506
  topCandidateCount: 1,
614
507
  punctuationThreshold: Infinity,
@@ -620,13 +513,20 @@ export class Whisper {
620
513
  endTokenThreshold: whisperAlignmentOptions.endTokenThreshold!,
621
514
  includeEndTokenInCandidates: false,
622
515
  timestampAccuracy: whisperAlignmentOptions.timestampAccuracy!,
623
- encoderProvider: whisperAlignmentOptions.encoderProvider!,
624
- decoderProvider: whisperAlignmentOptions.decoderProvider!,
625
516
  seed: undefined,
626
517
  }
627
518
 
519
+ logger.end()
520
+
628
521
  // Recognize
629
- const { timeline, allDecodedTokens } = await this.recognize(rawAudio, task, sourceLanguage, options, logitFilter)
522
+ const { timeline, allDecodedTokens } = await this.recognize(
523
+ rawAudio,
524
+ task,
525
+ sourceLanguage,
526
+ options,
527
+ logitFilter,
528
+ callbacks,
529
+ )
630
530
 
631
531
  {
632
532
  // If not all tokens were decoded, add the remaining ones to the timeline
@@ -675,44 +575,41 @@ export class Whisper {
675
575
  return timeline
676
576
  }
677
577
 
678
- async detectLanguage(audioFeatures: Onnx.Tensor, temperature: number): Promise<LanguageDetectionResults> {
578
+ async detectLanguage(
579
+ audioSamples: RawAudio,
580
+ options: WhisperLanguageDetectionOptions,
581
+ callbacks: SpeechLanguageDetectionCallbacks): Promise<LanguageDetectionResults> {
582
+
679
583
  if (!this.isMultiligualModel) {
680
584
  throw new Error('Language detection is only supported with multilingual models')
681
585
  }
682
586
 
683
- await this.initializeDecoderSessionIfNeeded()
684
-
685
- // Prepare and run decoder
686
- const logger = new Logger()
687
- await logger.startAsync('Detect language with Whisper model')
688
-
689
- const sotToken = this.tokenConfig.startOfTextToken
587
+ if (audioSamples.sampleRate !== 16000) {
588
+ throw new Error('Sample rate must be 16000 Hz')
589
+ }
690
590
 
691
- const initialTokens = [sotToken]
692
- const offset = 0
591
+ if (audioSamples.audioChannels.length !== 1) {
592
+ throw new Error('Audio must be mono')
593
+ }
693
594
 
694
- const Onnx = await import('onnxruntime-node')
595
+ await this.initializeContextIfNeeded(options, { logLevel: callbacks.logLevel })
695
596
 
696
- const initialKvDimensions = this.getKvDimensions(1, initialTokens.length)
697
- const kvCacheTensor = new Onnx.Tensor('float32', new Float32Array(initialKvDimensions[0] * initialKvDimensions[1] * initialKvDimensions[2] * initialKvDimensions[3]), initialKvDimensions)
597
+ // Prepare and run decoder
598
+ const logger = new Logger(callbacks.logLevel)
599
+ await logger.startAsync('Detect language with Whisper model')
600
+ logger.log(``)
698
601
 
699
- const tokensTensor = new Onnx.Tensor('int64', new BigInt64Array(initialTokens.map(token => BigInt(token))), [1, initialTokens.length])
700
- const offsetTensor = new Onnx.Tensor('int64', new BigInt64Array([BigInt(offset)]), [])
602
+ await this.encodeSamples(audioSamples.audioChannels[0], undefined, options, callbacks)
701
603
 
702
- const decoderInputs = {
703
- tokens: tokensTensor,
704
- audio_features: audioFeatures,
705
- kv_cache: kvCacheTensor,
706
- offset: offsetTensor
707
- }
604
+ const specialTokens = this.specialTokens
708
605
 
709
- const decoderOutputs = await this.textDecoder!.run(decoderInputs)
710
- const logitsBuffer = decoderOutputs['logits'].data as Float32Array
606
+ const initialTokens = [specialTokens.startOfTextToken]
711
607
 
712
- const tokenConfig = this.tokenConfig
608
+ await this.context!.decodeTokens(initialTokens, 0, options.threadCount!);
713
609
 
714
- const languageTokensLogits = Array.from(logitsBuffer.slice(tokenConfig.languageTokensStart, tokenConfig.languageTokensEnd))
715
- const languageTokensProbabilities = softmax(languageTokensLogits, temperature)
610
+ const logits = this.context!.getLogits()
611
+ const languageTokensLogits = logits.subarray(specialTokens.languageTokensStart, specialTokens.languageTokensEnd)
612
+ const languageTokensProbabilities = softmax(languageTokensLogits, options.temperature)
716
613
 
717
614
  const results: LanguageDetectionResults = []
718
615
 
@@ -732,72 +629,96 @@ export class Whisper {
732
629
  return results
733
630
  }
734
631
 
735
- async detectVoiceActivity(audioFeatures: Onnx.Tensor, temperature: number): Promise<number> {
736
- await this.initializeDecoderSessionIfNeeded()
632
+ async encodeSamples(samples: Float32Array, useCustomMelConverter = false, options: WhisperOptions, callbacks: OperationCallbacks) {
633
+ const logger = new Logger(callbacks.logLevel)
737
634
 
738
- // Prepare and run decoder
739
- const logger = new Logger()
740
- await logger.startAsync('Detect voice activity with Whisper model')
635
+ if (useCustomMelConverter) {
636
+ await logger.startAsync('Extract Mel spectrogram from audio part')
741
637
 
742
- const sotToken = this.tokenConfig.startOfTextToken
638
+ // Compute Mel spectrogram
639
+ const sampleRate = 16000
640
+ const rawAudio: RawAudio = { audioChannels: [samples], sampleRate: 16000 }
743
641
 
744
- const initialTokens = [sotToken]
745
- const offset = 0
642
+ const fftOrder = 400
643
+ const fftWindowSize = 400
644
+ const fftHopLength = 160
746
645
 
747
- const Onnx = await import('onnxruntime-node')
646
+ const melBandCount = this.melBandCount
647
+ const melFilterbank = this.melFilterbank
748
648
 
749
- const initialKvDimensions = this.getKvDimensions(1, initialTokens.length)
750
- const kvCacheTensor = new Onnx.Tensor('float32', new Float32Array(initialKvDimensions[0] * initialKvDimensions[1] * initialKvDimensions[2] * initialKvDimensions[3]), initialKvDimensions)
649
+ const maxAudioSamples = sampleRate * 30
650
+ const maxFrameCount = 3000
751
651
 
752
- const tokensTensor = new Onnx.Tensor('int64', new BigInt64Array(initialTokens.map(token => BigInt(token))), [1, initialTokens.length])
753
- const offsetTensor = new Onnx.Tensor('int64', new BigInt64Array([BigInt(offset)]), [])
652
+ const { melSpectrogram } = await computeMelSpectrogramUsingFilterbank(
653
+ rawAudio,
654
+ fftOrder,
655
+ fftWindowSize,
656
+ fftHopLength,
657
+ melFilterbank,
658
+ undefined,
659
+ callbacks
660
+ )
754
661
 
755
- const decoderInputs = {
756
- tokens: tokensTensor,
757
- audio_features: audioFeatures,
758
- kv_cache: kvCacheTensor,
759
- offset: offsetTensor
760
- }
662
+ const frameCount = Math.min(melSpectrogram.length, maxFrameCount)
663
+
664
+ // Flatten, transpose, apply logarithm, and normalize Mel spectrogram
665
+ await logger.startAsync('Process Mel spectrogram')
666
+
667
+ const flattenedLogMelSpectrogram = new Float32Array(frameCount * melBandCount)
668
+
669
+ let maxLogMel = -Infinity
761
670
 
762
- const decoderOutputs = await this.textDecoder!.run(decoderInputs)
763
- const logitsBuffer = decoderOutputs['logits'].data as Float32Array
671
+ for (let i = 0; i < melBandCount; i++) {
672
+ for (let j = 0; j < frameCount; j++) {
673
+ const mel = melSpectrogram[j][i]
674
+ const logMel = Math.log10(Math.max(mel, 1e-10))
764
675
 
765
- const tokenConfig = this.tokenConfig
676
+ if (logMel > maxLogMel) {
677
+ maxLogMel = logMel
678
+ }
766
679
 
767
- const logits = Array.from(logitsBuffer)
680
+ flattenedLogMelSpectrogram[(i * frameCount) + j] = logMel
681
+ }
682
+ }
768
683
 
769
- const probabilities = softmax(logits, temperature)
684
+ for (let i = 0; i < flattenedLogMelSpectrogram.length; i++) {
685
+ const logMel = flattenedLogMelSpectrogram[i]
686
+ const normalizedLogMel = (Math.max(logMel, maxLogMel - 8) + 4) / 4
687
+
688
+ flattenedLogMelSpectrogram[i] = normalizedLogMel
689
+ }
770
690
 
771
- const noSpeechProbability = probabilities[tokenConfig.nonSpeechToken]
691
+ await logger.startAsync('Encode Mel spectrogram')
772
692
 
773
- return 1.0 - noSpeechProbability
693
+ await this.context!.encodeLogMelSpectrogram(flattenedLogMelSpectrogram, melBandCount)
694
+ } else {
695
+ await logger.startAsync('Encode samples')
696
+
697
+ await this.context!.encodeSamples(samples, options.threadCount!)
698
+ }
699
+
700
+ logger.end()
774
701
  }
775
702
 
776
703
  // Decode tokens using the decoder model
777
704
  async decodeTokens(
778
- audioFeatures: Onnx.Tensor,
779
705
  initialTokens: number[],
780
706
  audioDuration: number,
781
707
  isFirstPart: boolean,
782
708
  isFinalPart: boolean,
783
709
  options: WhisperOptions,
784
- logitFilter?: WhisperLogitFilter) {
710
+ logitFilter: WhisperLogitFilter | undefined,
711
+ callbacks: RecognitionCallbacks) {
785
712
 
786
- // Initialize
787
- await this.initializeTokenizerIfNeeded()
788
- await this.initializeDecoderSessionIfNeeded()
789
-
790
- const logger = new Logger()
713
+ const logger = new Logger(callbacks.logLevel)
791
714
 
792
715
  await logger.startAsync('Decode text tokens with Whisper decoder model')
793
716
 
794
717
  options = extendDeep(defaultWhisperOptions, options)
795
718
 
796
- const Onnx = await import('onnxruntime-node')
797
-
798
719
  // Get token information
799
- const endOfTextToken = this.tokenConfig.endOfTextToken
800
- const timestampTokensStart = this.tokenConfig.timestampTokensStart
720
+ const endOfTextToken = this.specialTokens.endOfTextToken
721
+ const timestampTokensStart = this.specialTokens.timestampTokensStart
801
722
 
802
723
  const suppressedTextTokens = this.getSuppressedTextTokens()
803
724
  const suppressedMetadataTokens = this.getSuppressedMetadataTokens()
@@ -808,15 +729,13 @@ export class Whisper {
808
729
 
809
730
  // Initialize variables for decoding loop
810
731
  let decodedTokens = initialTokens.slice()
811
- const initialKvDimensions = this.getKvDimensions(1, decodedTokens.length)
812
- let kvCacheTensor = new Onnx.Tensor('float32', new Float32Array(initialKvDimensions[0] * initialKvDimensions[1] * initialKvDimensions[2] * initialKvDimensions[3]), initialKvDimensions)
813
732
 
814
- let decodedTokensTimestampLogits: number[][] = []
733
+ let decodedTokensTimestampLogits: Float32Array[] = []
815
734
  let decodedTokensConfidence: number[] = []
816
- let decodedTokensCrossAttentionQKs: OnnxLikeFloat32Tensor[] = []
735
+ let decodedTokensCrossAttentionQKs: CrossAttentionQKs[] = []
817
736
 
818
737
  for (let i = 0; i < decodedTokens.length; i++) {
819
- decodedTokensTimestampLogits.push(new Array(1501)) // Should the length be 1500 instead?
738
+ decodedTokensTimestampLogits.push(new Float32Array(1501)) // Should the length be 1500 instead?
820
739
  decodedTokensConfidence.push(1.0)
821
740
  decodedTokensCrossAttentionQKs.push(undefined as any)
822
741
  }
@@ -826,11 +745,15 @@ export class Whisper {
826
745
  let bufferedTokensToPrint: number[] = []
827
746
 
828
747
  // Define method to add a token to output
829
- function addToken(tokenToAdd: number, timestampLogits: number[], confidence: number, crossAttentionQKs: OnnxLikeFloat32Tensor) {
748
+ const addToken = (tokenToAdd: number, timestampLogits: Float32Array, confidence: number, crossAttentionQKs: CrossAttentionQKs) => {
830
749
  decodedTokens.push(tokenToAdd)
831
750
  decodedTokensTimestampLogits.push(timestampLogits)
832
751
  decodedTokensConfidence.push(confidence)
833
752
  decodedTokensCrossAttentionQKs.push(crossAttentionQKs)
753
+
754
+ if (callbacks?.onToken) {
755
+ callbacks.onToken(tokenToAdd, this.tokenToBytes(tokenToAdd))
756
+ }
834
757
  }
835
758
 
836
759
  const maxTokensPerPart = Math.min(options.maxTokensPerPart!, largestMaximumTokensPerPart)
@@ -842,6 +765,8 @@ export class Whisper {
842
765
 
843
766
  // Start decoding loop
844
767
  for (let decodedTokenCount = 0; decodedTokenCount < maxTokensPerPart; decodedTokenCount++) {
768
+ callbacks?.abortSignal?.throwIfAborted()
769
+
845
770
  if (decodedTokenCount > 0) {
846
771
  decodedTokensDecodingTime.push(tokenDecodingTimeTimer.getElapsedTimeAndRestart())
847
772
  }
@@ -849,54 +774,24 @@ export class Whisper {
849
774
  const isInitialState = decodedTokens.length === initialTokens.length
850
775
  const atLeastOneTextTokenDecoded = decodedTokens.slice(initialTokens.length).some(token => this.isTextToken(token))
851
776
 
852
- // If not in initial state, reshape KV Cache tensor to accomodate a new output token
853
- if (!isInitialState) {
854
- const dims = kvCacheTensor.dims
855
-
856
- const currentKvCacheGroups = splitFloat32Array(kvCacheTensor.data as Float32Array, dims[2] * dims[3])
857
-
858
- const reshapedKvCacheTensor = new Onnx.Tensor('float32', new Float32Array(dims[0] * dims[1] * (decodedTokens.length) * dims[3]), [dims[0], dims[1], decodedTokens.length, dims[3]])
859
- const reshapedKvCacheGroups = splitFloat32Array(reshapedKvCacheTensor.data, decodedTokens.length * dims[3])
860
-
861
- for (let i = 0; i < dims[0]; i++) {
862
- reshapedKvCacheGroups[i].set(currentKvCacheGroups[i])
863
- }
864
-
865
- kvCacheTensor = reshapedKvCacheTensor
866
- }
867
-
868
777
  // Prepare values for decoder
869
778
  const tokensToDecode = isInitialState ? decodedTokens : [decodedTokens[decodedTokens.length - 1]]
870
779
  const offset = isInitialState ? 0 : decodedTokens.length
871
780
 
872
- const tokensTensor = new Onnx.Tensor('int64', new BigInt64Array(tokensToDecode.map(token => BigInt(token))), [1, tokensToDecode.length])
873
- const offsetTensor = new Onnx.Tensor('int64', new BigInt64Array([BigInt(offset)]), [])
874
-
875
- const decoderInputs = {
876
- tokens: tokensTensor,
877
- audio_features: audioFeatures,
878
- kv_cache: kvCacheTensor,
879
- offset: offsetTensor
880
- }
781
+ //// Infer
782
+ const tokenInferenceTimer = new Timer()
881
783
 
882
- //// Infer with ONNX decoder model
883
- const tokenInferenceTimeTimer = new Timer()
784
+ await this.context!.decodeTokens(tokensToDecode, offset, options.threadCount!)
884
785
 
885
- const decoderOutputs = await this.textDecoder!.run(decoderInputs)
786
+ decodedTokensInferenceTime.push(tokenInferenceTimer.elapsedTime)
886
787
 
887
- decodedTokensInferenceTime.push(tokenInferenceTimeTimer.elapsedTime)
888
-
889
- // Extract decoder model results
890
- const logitsBuffer = decoderOutputs['logits'].data as Float32Array
891
- kvCacheTensor = decoderOutputs['output_kv_cache'] as any
892
-
893
- const crossAttentionQKsForTokenOnnx = decoderOutputs['cross_attention_qks']
894
- const crossAttentionQKsForToken = makeOnnxLikeFloat32Tensor(crossAttentionQKsForTokenOnnx)
895
- crossAttentionQKsForTokenOnnx.dispose()
788
+ // Get cross-attention QKs
789
+ //const getCrossAttentionQKsTimer = new Timer()
790
+ const tokenCrossAttentionQKs = await this.context!.getCrossAttentionQKs()
791
+ //getCrossAttentionQKsTimer.logAndRestart('getCrossAttentionQKs')
896
792
 
897
793
  // Get logits
898
- const resultLogitsFloatArrays = splitFloat32Array(logitsBuffer, logitsBuffer.length / decoderOutputs['logits'].dims[1])
899
- const allTokenLogits = Array.from(resultLogitsFloatArrays[resultLogitsFloatArrays.length - 1])
794
+ const allTokenLogits = this.context!.getLogits()
900
795
 
901
796
  // Suppress metadata tokens in the suppression set
902
797
  for (const suppressedTokenIndex of suppressedMetadataTokens) {
@@ -908,7 +803,7 @@ export class Whisper {
908
803
  allTokenLogits[endOfTextToken] = -Infinity
909
804
  }
910
805
 
911
- const timestampTokenLogits = allTokenLogits.slice(timestampTokensStart)
806
+ const timestampTokenLogits = allTokenLogits.subarray(timestampTokensStart)
912
807
 
913
808
  const decodeTimestampTokenIfNeeded = () => {
914
809
  // Try to decode a timestamp token, if needed
@@ -921,7 +816,7 @@ export class Whisper {
921
816
  // If this is the first token in the part, unconditionally decode a timestamp token
922
817
  // for time 0.0
923
818
  if (isInitialState) {
924
- addToken(timestampTokensStart, timestampTokenLogits, 1.0, crossAttentionQKsForToken)
819
+ addToken(timestampTokensStart, timestampTokenLogits, 1.0, tokenCrossAttentionQKs)
925
820
 
926
821
  return true
927
822
  }
@@ -937,26 +832,25 @@ export class Whisper {
937
832
  }
938
833
 
939
834
  // Derive token probabilities
940
- const allTokenProbabilities = softmax(allTokenLogits as any, 1.0)
941
- const allTokenLogProbabilities = logOfVector(allTokenProbabilities)
942
-
943
- const nonTimestampTokenLogProbs = allTokenLogProbabilities.slice(0, timestampTokensStart)
835
+ const allTokenProbabilities = softmax(allTokenLogits, 1.0)
836
+ const nonTimestampTokenProbs = allTokenProbabilities.subarray(0, timestampTokensStart)
944
837
 
945
- // Find highest non-timestamp token
946
- const indexOfMaxNonTimestampLogProb = indexOfMax(nonTimestampTokenLogProbs)
947
- const valueOfMaxNonTimestampLogProb = nonTimestampTokenLogProbs[indexOfMaxNonTimestampLogProb]
838
+ // Find highest probability non-timestamp token
839
+ const indexOfMaxNonTimestampProb = indexOfMax(nonTimestampTokenProbs)
840
+ const valueOfMaxNonTimestampProb = nonTimestampTokenProbs[indexOfMaxNonTimestampProb]
841
+ const valueOfMaxNonTimestampLogProb = Math.log(valueOfMaxNonTimestampProb)
948
842
 
949
- // Find highest timestamp token
950
- const timestampTokenLogProbs = allTokenLogProbabilities.slice(timestampTokensStart)
951
- const indexOfMaxTimestampLogProb = indexOfMax(timestampTokenLogProbs)
843
+ // Find highest probability timestamp token
844
+ const timestampTokenProbs = allTokenProbabilities.subarray(timestampTokensStart)
845
+ const indexOfMaxTimestampProb = indexOfMax(timestampTokenProbs)
952
846
 
953
- // Compute the log of the sum of exponentials of the log probabilities
954
- // of the timestamp tokens
955
- const logSumExpOfTimestampTokenLogProbs = logSumExp(timestampTokenLogProbs)
847
+ // Compute the log of the sum of probabilities of the timestamp tokens
848
+ const sumOfTimestampTokenProbs = sumVector(timestampTokenProbs)
849
+ const logSumOfTimestampTokenProbs = Math.log(sumOfTimestampTokenProbs)
956
850
 
957
851
  // If the sum isn't greater than the log probability of the highest non-timestamp token,
958
852
  // don't decode a timestamp
959
- if (logSumExpOfTimestampTokenLogProbs <= valueOfMaxNonTimestampLogProb) {
853
+ if (logSumOfTimestampTokenProbs <= valueOfMaxNonTimestampLogProb) {
960
854
  return false
961
855
  }
962
856
 
@@ -969,15 +863,15 @@ export class Whisper {
969
863
  const previousTokenTimestampLogits = decodedTokensTimestampLogits[decodedTokensTimestampLogits.length - 1]
970
864
  const previousTokenConfidence = decodedTokensConfidence[decodedTokensConfidence.length - 1]
971
865
 
972
- addToken(previousToken, previousTokenTimestampLogits, previousTokenConfidence, crossAttentionQKsForToken)
866
+ addToken(previousToken, previousTokenTimestampLogits, previousTokenConfidence, tokenCrossAttentionQKs)
973
867
 
974
868
  lastTimestampTokenIndex = decodedTokens.length
975
869
  } else {
976
870
  // Otherwise decode the highest probability timestamp
977
- const timestampToken = timestampTokensStart + indexOfMaxTimestampLogProb
871
+ const timestampToken = timestampTokensStart + indexOfMaxTimestampProb
978
872
  const confidence = allTokenProbabilities[timestampToken]
979
873
 
980
- addToken(timestampToken, timestampTokenLogits, confidence, crossAttentionQKsForToken)
874
+ addToken(timestampToken, timestampTokenLogits, confidence, tokenCrossAttentionQKs)
981
875
  }
982
876
 
983
877
  return true
@@ -993,7 +887,7 @@ export class Whisper {
993
887
  }
994
888
 
995
889
  // Decode a non-timestamp token
996
- let nonTimestampTokenLogits = allTokenLogits.slice(0, timestampTokensStart)
890
+ let nonTimestampTokenLogits: Float32Array<ArrayBufferLike> = allTokenLogits.slice(0, timestampTokensStart)
997
891
 
998
892
  let shouldDecodeEndfOfTextToken = false
999
893
 
@@ -1029,7 +923,7 @@ export class Whisper {
1029
923
 
1030
924
  // If filter caused all word token logits to be -Infinity, then there is no
1031
925
  // other token to decode. Fall back to accept end-of-text
1032
- if (nonTimestampTokenLogits.slice(0, endOfTextToken).every(logit => logit === -Infinity)) {
926
+ if (nonTimestampTokenLogits.subarray(0, endOfTextToken).every(logit => logit === -Infinity)) {
1033
927
  shouldDecodeEndfOfTextToken = true
1034
928
  }
1035
929
  } else {
@@ -1047,7 +941,7 @@ export class Whisper {
1047
941
  // If end-of-text token should be decoded, then add it and break
1048
942
  // out of the loop
1049
943
  if (shouldDecodeEndfOfTextToken) {
1050
- addToken(endOfTextToken, timestampTokenLogits, 1.0, crossAttentionQKsForToken)
944
+ addToken(endOfTextToken, timestampTokenLogits, 1.0, tokenCrossAttentionQKs)
1051
945
 
1052
946
  break
1053
947
  }
@@ -1150,7 +1044,7 @@ export class Whisper {
1150
1044
  const chosenToken = topCandidates[chosenCandidateRank].token
1151
1045
  const chosenTokenConfidence = topCandidateProbabilities[chosenCandidateRank]
1152
1046
 
1153
- addToken(chosenToken, timestampTokenLogits, chosenTokenConfidence, crossAttentionQKsForToken)
1047
+ addToken(chosenToken, timestampTokenLogits, chosenTokenConfidence, tokenCrossAttentionQKs)
1154
1048
 
1155
1049
  // If chosen token is the end-of-text token, break
1156
1050
  if (chosenToken === endOfTextToken) {
@@ -1189,8 +1083,8 @@ export class Whisper {
1189
1083
 
1190
1084
  decodedTokens = decodedTokens.slice(0, sliceEndTokenIndex)
1191
1085
  decodedTokensTimestampLogits = decodedTokensTimestampLogits.slice(0, sliceEndTokenIndex)
1192
- decodedTokensCrossAttentionQKs = decodedTokensCrossAttentionQKs.slice(0, sliceEndTokenIndex)
1193
1086
  decodedTokensConfidence = decodedTokensConfidence.slice(0, sliceEndTokenIndex)
1087
+ decodedTokensCrossAttentionQKs = decodedTokensCrossAttentionQKs.slice(0, sliceEndTokenIndex)
1194
1088
 
1195
1089
  decodedTokensDecodingTime = decodedTokensDecodingTime.slice(0, sliceEndTokenIndex)
1196
1090
  decodedTokensInferenceTime = decodedTokensInferenceTime.slice(0, sliceEndTokenIndex)
@@ -1214,88 +1108,6 @@ export class Whisper {
1214
1108
  }
1215
1109
  }
1216
1110
 
1217
- // Encode audio using the encoder model
1218
- async encodeAudio(rawAudio: RawAudio) {
1219
- await this.initializeEncoderSessionIfNeeded()
1220
-
1221
- const Onnx = await import('onnxruntime-node')
1222
-
1223
- const logger = new Logger()
1224
-
1225
- const audioSamples = rawAudio.audioChannels[0]
1226
- const sampleRate = rawAudio.sampleRate
1227
-
1228
- const fftOrder = 400
1229
- const fftWindowSize = 400
1230
- const fftHopLength = 160
1231
-
1232
- const filterbankCount = this.filterbankCount
1233
- const filterbanks = this.filterbanks
1234
-
1235
- const maxAudioSamples = sampleRate * 30
1236
- const maxAudioFrames = 3000
1237
-
1238
- if (sampleRate !== 16000) {
1239
- throw new Error('Audio must have a sample rate of 16000 Hz')
1240
- }
1241
-
1242
- if (audioSamples.length > maxAudioSamples) {
1243
- throw new Error(`Audio part is longer than 30 seconds`)
1244
- }
1245
-
1246
- await logger.startAsync('Extract Mel spectrogram from audio part')
1247
-
1248
- // Pad audio samples to ensure that have a duration of 30 seconds
1249
- const paddedAudioSamples = new Float32Array(maxAudioSamples)
1250
- paddedAudioSamples.set(audioSamples, 0)
1251
-
1252
- const rawAudioPart: RawAudio = { audioChannels: [paddedAudioSamples], sampleRate }
1253
-
1254
- // Compute Mel spectrogram
1255
- const { melSpectrogram } = await computeMelSpectrogramUsingFilterbanks(rawAudioPart, fftOrder, fftWindowSize, fftHopLength, filterbanks)
1256
-
1257
- // Flatten, transpose, apply logarithm and normalize Mel spectrogram
1258
- await logger.startAsync('Process Mel spectrogram')
1259
-
1260
- const flattenedLogMelSpectrogram = new Float32Array(maxAudioFrames * filterbankCount)
1261
-
1262
- let maxLogMel = -Infinity
1263
-
1264
- for (let i = 0; i < filterbankCount; i++) {
1265
- for (let j = 0; j < maxAudioFrames; j++) {
1266
- const mel = melSpectrogram[j][i]
1267
- const logMel = Math.log10(Math.max(mel, 1e-10))
1268
-
1269
- if (logMel > maxLogMel) {
1270
- maxLogMel = logMel
1271
- }
1272
-
1273
- flattenedLogMelSpectrogram[(i * maxAudioFrames) + j] = logMel
1274
- }
1275
- }
1276
-
1277
- for (let i = 0; i < flattenedLogMelSpectrogram.length; i++) {
1278
- const logMel = flattenedLogMelSpectrogram[i]
1279
- const normalizedLogMel = (Math.max(logMel, maxLogMel - 8) + 4) / 4
1280
-
1281
- flattenedLogMelSpectrogram[i] = normalizedLogMel
1282
- }
1283
-
1284
- // Run the encoder model
1285
- await logger.startAsync('Encode Mel spectrogram with Whisper encoder model')
1286
-
1287
- const inputTensor = new Onnx.Tensor('float32', flattenedLogMelSpectrogram, [1, filterbankCount, maxAudioFrames])
1288
-
1289
- const encoderInputs = { mel: inputTensor }
1290
-
1291
- const encoderOutputs = await this.audioEncoder!.run(encoderInputs)
1292
- const encodedAudioFeatures = encoderOutputs['output']
1293
-
1294
- logger.end()
1295
-
1296
- return encodedAudioFeatures
1297
- }
1298
-
1299
1111
  tokenTimelineToWordTimeline(tokenTimeline: Timeline, language: string): Timeline {
1300
1112
  function isSeparatorCharacter(char: string) {
1301
1113
  const nonSeparatingPunctuation = [`'`, `-`, `.`, `·`, `•`]
@@ -1304,7 +1116,7 @@ export class Whisper {
1304
1116
  return false
1305
1117
  }
1306
1118
 
1307
- return isWhitespace(char) || includesPunctuation(char)
1119
+ return isAllWhitespace(char) || includesPunctuation(char)
1308
1120
  }
1309
1121
 
1310
1122
  function startsWithSeparatorCharacter(text: string) {
@@ -1410,6 +1222,7 @@ export class Whisper {
1410
1222
  const tokenIndex = tokenMappingEntry.source
1411
1223
  const token = tokens[tokenIndex]
1412
1224
  const tokenConfidence = tokensConfidence ? tokensConfidence[tokenIndex] : undefined
1225
+
1413
1226
  const tokenText = this.tokenToText(token, true)
1414
1227
 
1415
1228
  let startTime = startTimeOffset + (tokenMappingEntry.dest * 0.02)
@@ -1437,41 +1250,48 @@ export class Whisper {
1437
1250
  return tokenTimeline
1438
1251
  }
1439
1252
 
1440
- async findAlignmentPathFromQKs(qksTensors: OnnxLikeFloat32Tensor[], tokens: number[], segmentStartFrame: number, segmentEndFrame: number, headIndexes?: number[]) {
1441
- const segmentFrameCount = segmentEndFrame - segmentStartFrame
1253
+ async findAlignmentPathFromCrossAttentionQKs(
1254
+ crossAttentionQKs: CrossAttentionQKs[],
1255
+ tokens: number[],
1256
+ partStartFrameIndex: number,
1257
+ partEndFrameIndex: number,
1258
+ alignmentHeadsIndexesToUse?: number[]) {
1442
1259
 
1443
- if (segmentFrameCount === 0 || tokens.length === 0 || qksTensors.length === 0) {
1260
+ const partFrameCount = partEndFrameIndex - partStartFrameIndex
1261
+
1262
+ if (partFrameCount === 0 || tokens.length === 0 || crossAttentionQKs.length === 0 || crossAttentionQKs[0].data.length === 0) {
1444
1263
  return []
1445
1264
  }
1446
1265
 
1447
- const tokenCount = qksTensors.length
1448
- const layerCount = qksTensors[0].dims[0]
1449
- const headCount = qksTensors[0].dims[2]
1450
- const frameCount = qksTensors[0].dims[4]
1266
+ // crossAttentionQKs internal dimensions: [heads, tokens = 1, frames]
1267
+ const tokenCount = crossAttentionQKs.length
1268
+
1269
+ const headCount = Number(crossAttentionQKs[0].dimensions[0])
1270
+ const frameCount = Number(crossAttentionQKs[0].dimensions[2])
1451
1271
 
1452
- if (!headIndexes) {
1453
- headIndexes = getIntegerRange(0, layerCount * headCount)
1272
+ if (!alignmentHeadsIndexesToUse) {
1273
+ alignmentHeadsIndexesToUse = getIntegerRange(0, headCount)
1454
1274
  }
1455
1275
 
1456
- // Load attention head weights from tensors
1457
- const attentionHeads: Float32Array[][] = [] // structure: [heads, tokens, frames]
1276
+ // Initialize attention head arrays
1277
+ const attentionHeads: Float32Array[][] = [] // dimensions: [heads, tokens, frames]
1458
1278
 
1459
- for (const headIndex of headIndexes) {
1460
- const attentionHead: Float32Array[] = [] // structure: [tokens, frames]
1279
+ for (const headIndex of alignmentHeadsIndexesToUse) {
1280
+ const head: Float32Array[] = []
1461
1281
 
1462
- for (let tokenIndex = 0; tokenIndex < tokenCount; tokenIndex++) {
1463
- const bufferOffset = headIndex * frameCount
1464
- const startIndexInBuffer = bufferOffset + segmentStartFrame
1465
- const endIndexInBuffer = bufferOffset + segmentEndFrame
1282
+ const startIndexInBuffer = headIndex * frameCount
1283
+ const endIndexInBuffer = startIndexInBuffer + partFrameCount
1466
1284
 
1467
- const framesForHead = qksTensors[tokenIndex].data.slice(startIndexInBuffer, endIndexInBuffer)
1285
+ for (let tokenIndex = 0; tokenIndex < tokenCount; tokenIndex++) {
1286
+ const data = crossAttentionQKs[tokenIndex].data.subarray(startIndexInBuffer, endIndexInBuffer)
1468
1287
 
1469
- attentionHead.push(framesForHead)
1288
+ head.push(data)
1470
1289
  }
1471
1290
 
1472
- attentionHeads.push(attentionHead)
1291
+ attentionHeads.push(head)
1473
1292
  }
1474
1293
 
1294
+ // Initialize settings
1475
1295
  const applySoftmax = true
1476
1296
  const normalize = true
1477
1297
  const applyMedianFilter = true
@@ -1526,13 +1346,13 @@ export class Whisper {
1526
1346
  }
1527
1347
  }
1528
1348
 
1529
- // Compute the mean of the selected attention heads for all layers
1349
+ // Compute the mean of the selected attention heads for each frame
1530
1350
  const frameMeansForToken: Float32Array[] = []
1531
1351
 
1532
1352
  for (let tokenIndex = 0; tokenIndex < tokenCount; tokenIndex++) {
1533
- const meansForFrames = new Float32Array(segmentFrameCount)
1353
+ const meansForFrames = new Float32Array(partFrameCount)
1534
1354
 
1535
- for (let frameIndex = 0; frameIndex < segmentFrameCount; frameIndex++) {
1355
+ for (let frameIndex = 0; frameIndex < partFrameCount; frameIndex++) {
1536
1356
  let sum = 0
1537
1357
 
1538
1358
  for (const head of attentionHeads) {
@@ -1549,7 +1369,7 @@ export class Whisper {
1549
1369
 
1550
1370
  // Anchor timestamp tokens timestamps to their original values, if enabled
1551
1371
  if (anchorTimestampTokens) {
1552
- const timestampTokensStart = this.tokenConfig.timestampTokensStart
1372
+ const timestampTokensStart = this.specialTokens.timestampTokensStart
1553
1373
 
1554
1374
  for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex++) {
1555
1375
  const token = tokens[tokenIndex]
@@ -1557,7 +1377,7 @@ export class Whisper {
1557
1377
  if (this.isTimestampToken(token)) {
1558
1378
  let timestampFrame = token - timestampTokensStart
1559
1379
 
1560
- timestampFrame = clip(timestampFrame, segmentStartFrame, segmentEndFrame - 1)
1380
+ timestampFrame = clip(timestampFrame, partStartFrameIndex, partEndFrameIndex - 1)
1561
1381
 
1562
1382
  frameMeansForToken[tokenIndex][timestampFrame] = 100
1563
1383
  }
@@ -1566,37 +1386,100 @@ export class Whisper {
1566
1386
 
1567
1387
  // Perform DTW to align tokens indexes to frame indexes
1568
1388
  const tokenIndexes = getIntegerRange(0, tokenCount)
1569
- const frameIndexes = getIntegerRange(0, segmentFrameCount)
1389
+ const frameIndexes = getIntegerRange(0, partFrameCount)
1570
1390
 
1571
1391
  let { path } = alignDTWWindowed(tokenIndexes, frameIndexes, (tokenIndex, frameIndex) => {
1572
1392
  return -frameMeansForToken[tokenIndex][frameIndex]
1573
- }, segmentFrameCount)
1393
+ }, partFrameCount)
1574
1394
 
1575
- path = path.map(entry => ({ source: entry.source, dest: segmentStartFrame + entry.dest }))
1395
+ path = path.map(entry => ({ source: entry.source, dest: partStartFrameIndex + entry.dest }))
1576
1396
 
1577
1397
  return path
1578
1398
  }
1579
1399
 
1580
- async initializeIfNeeded() {
1581
- await this.initializeTokenizerIfNeeded()
1582
- await this.initializeEncoderSessionIfNeeded()
1583
- await this.initializeDecoderSessionIfNeeded()
1400
+ async initializeIfNeeded(options: WhisperOptions, callbacks: OperationCallbacks) {
1401
+ await this.initializeTokenizerIfNeeded(callbacks)
1402
+ await this.initializeContextIfNeeded(options, callbacks)
1584
1403
  }
1585
1404
 
1586
- async initializeTokenizerIfNeeded() {
1405
+ async initializeContextIfNeeded(options: WhisperOptions, callbacks: OperationCallbacks) {
1406
+ if (this.context) {
1407
+ return
1408
+ }
1409
+
1410
+ const logger = new Logger(callbacks.logLevel)
1411
+
1412
+ const modelId = this.modelId
1413
+ const modelFilePath = this.modelPath
1414
+ const libPath = this.libPath
1415
+
1416
+ let libFilePath: string
1417
+
1418
+ if (process.platform === 'win32') {
1419
+ libFilePath = resolvePath(libPath, 'whisper.dll')
1420
+ } else if (process.platform === 'darwin') {
1421
+ libFilePath = resolvePath(libPath, 'libwhisper.0.dylib')
1422
+ } else if (process.platform === 'linux') {
1423
+ libFilePath = resolvePath(libPath, 'libwhisper.so.0')
1424
+ } else {
1425
+ throw new Error(`Unsupported platform`)
1426
+ }
1427
+
1428
+ await logger.startAsync(`Load whisper.cpp model '${modelId}'`)
1429
+
1430
+ this.context = new WhisperCppContext()
1431
+
1432
+ let alignmentHeadsPreset: WhisperAlignmentHeadsPreset
1433
+ let alignmentHeadsTopCount = 0
1434
+
1435
+ if (options.timestampAccuracy == 'high') {
1436
+ alignmentHeadsPreset = WhisperAlignmentHeadsPreset.WHISPER_AHEADS_N_TOP_MOST
1437
+ alignmentHeadsTopCount = whisperModelIdToTextLayerCount[modelId]
1438
+ } else {
1439
+ alignmentHeadsPreset = whisperModelIdToAlignmentHeadsPreset[modelId]
1440
+ }
1441
+
1442
+ const contextOptions: WhisperCppContextOptions = {
1443
+ libFilePath,
1444
+ modelFilePath,
1445
+
1446
+ // Force GPU option to be enabled for all GPU and CPU builds
1447
+ // The `enableGPU` option in `WhisperOptions` doesn't impact this
1448
+ enableGPU: true,
1449
+
1450
+ // Flash attention must be disabled for timestamp extraction to be possible
1451
+ // in the way it's done here
1452
+ enableFlashAttention: false,
1453
+
1454
+ // Device index is passed. Defaults to 0
1455
+ gpuDeviceIndex: options.gpuDeviceIndex!,
1456
+
1457
+ alignmentHeadsPreset,
1458
+ alignmentHeadsTopCount,
1459
+
1460
+ //logLevel: logger.logLevelGreaterOrEqualTo('trace') ? GgmlLogLevel.GGML_LOG_LEVEL_INFO : GgmlLogLevel.GGML_LOG_LEVEL_WARN,
1461
+ logLevel: GgmlLogLevel.GGML_LOG_LEVEL_WARN,
1462
+ }
1463
+
1464
+ await this.context.initialize(contextOptions)
1465
+
1466
+ logger.end()
1467
+ }
1468
+
1469
+ async initializeTokenizerIfNeeded(callbacks: LoadPackageCallbacks) {
1587
1470
  if (this.tiktoken) {
1588
1471
  return
1589
1472
  }
1590
1473
 
1591
- const logger = new Logger()
1474
+ const logger = new Logger(callbacks.logLevel)
1592
1475
  await logger.startAsync('Load tokenizer data')
1593
1476
 
1594
- const tiktokenModulePackagePath = await loadPackage('whisper-tiktoken-data')
1477
+ const tiktokenModulePackagePath = await loadPackage('whisper-tiktoken-data', callbacks)
1595
1478
 
1596
1479
  const tiktokenDataFilePath = joinPath(tiktokenModulePackagePath, this.isMultiligualModel ? 'multilingual.tiktoken' : 'gpt2.tiktoken')
1597
1480
  let tiktokenData = await readFileAsUtf8(tiktokenDataFilePath)
1598
1481
 
1599
- const tokenConfig = this.tokenConfig
1482
+ const tokenConfig = this.specialTokens
1600
1483
 
1601
1484
  const metadataTokens: Record<number, string> = {
1602
1485
  [tokenConfig.endOfTextToken]: '[EndOfText]',
@@ -1616,7 +1499,7 @@ export class Whisper {
1616
1499
  const languageTokenCount = tokenConfig.languageTokensEnd - tokenConfig.languageTokensStart
1617
1500
 
1618
1501
  for (let i = 0; i < languageTokenCount; i++) {
1619
- const tokenIndex = this.tokenConfig.languageTokensStart + i
1502
+ const tokenIndex = this.specialTokens.languageTokensStart + i
1620
1503
 
1621
1504
  metadataTokens[tokenIndex] = `[Language_${i}]`
1622
1505
  }
@@ -1624,7 +1507,7 @@ export class Whisper {
1624
1507
  const timestampTokensCount = 1501
1625
1508
 
1626
1509
  for (let i = 0; i <= timestampTokensCount; i++) {
1627
- const tokenIndex = this.tokenConfig.timestampTokensStart + i
1510
+ const tokenIndex = this.specialTokens.timestampTokensStart + i
1628
1511
  const tokenTime = this.timestampTokenToSeconds(tokenIndex)
1629
1512
 
1630
1513
  metadataTokens[tokenIndex] = `[Timestamp_${tokenTime.toFixed(2)}]`
@@ -1645,76 +1528,14 @@ export class Whisper {
1645
1528
  logger.end()
1646
1529
  }
1647
1530
 
1648
- async initializeEncoderSessionIfNeeded() {
1649
- if (this.audioEncoder) {
1650
- return
1651
- }
1652
-
1653
- const logger = new Logger()
1654
-
1655
- const onnxSessionOptions = getOnnxSessionOptions({ executionProviders: this.encoderExecutionProviders })
1656
-
1657
- const onnxProvidersString = onnxSessionOptions.executionProviders!.length > 0 ? `${onnxSessionOptions.executionProviders!.join(', ')}` : `default`
1658
-
1659
- await logger.startAsync(`Create encoder inference session for model '${this.modelName}' (ONNX provider: ${onnxProvidersString})`)
1660
-
1661
- const encoderFilePath = joinPath(this.modelDir, 'encoder.onnx')
1662
-
1663
- const Onnx = await import('onnxruntime-node')
1664
-
1665
- this.audioEncoder = await Onnx.InferenceSession.create(encoderFilePath, onnxSessionOptions)
1666
-
1667
- logger.end()
1668
- }
1669
-
1670
- async initializeDecoderSessionIfNeeded() {
1671
- if (this.textDecoder) {
1672
- return
1673
- }
1674
-
1675
- const logger = new Logger()
1676
-
1677
- const onnxSessionOptions = getOnnxSessionOptions({ executionProviders: this.decoderExecutionProviders })
1678
-
1679
- const onnxProvidersString = onnxSessionOptions.executionProviders!.length > 0 ? `${onnxSessionOptions.executionProviders!.join(', ')}` : `default`
1680
-
1681
- await logger.startAsync(`Create decoder inference session for model '${this.modelName}' (ONNX provider: ${onnxProvidersString})`)
1682
-
1683
- const decoderFilePath = joinPath(this.modelDir, 'decoder.onnx')
1684
-
1685
- const Onnx = await import('onnxruntime-node')
1686
-
1687
- this.textDecoder = await Onnx.InferenceSession.create(decoderFilePath, onnxSessionOptions)
1688
-
1689
- logger.end()
1690
- }
1691
-
1692
- getKvDimensions(groupCount: number, length: number) {
1693
- const modelName = this.modelName
1694
-
1695
- if (modelName === 'tiny' || modelName === 'tiny.en') {
1696
- return [8, groupCount, length, 384]
1697
- } else if (modelName === 'base' || modelName === 'base.en') {
1698
- return [12, groupCount, length, 512]
1699
- } else if (modelName === 'small' || modelName === 'small.en') {
1700
- return [24, groupCount, length, 768]
1701
- } else if (modelName === 'medium' || modelName === 'medium.en') {
1702
- return [48, groupCount, length, 1024]
1703
- } else if (modelName === 'large-v1' || modelName === 'large-v2' || modelName === 'large-v3' || modelName === 'large-v3-turbo') {
1704
- return [64, groupCount, length, 1280]
1705
- } else {
1706
- throw new Error(`Unsupported model: ${modelName}`)
1707
- }
1708
- }
1709
-
1710
1531
  getTextStartTokens(language: string, task: WhisperTask, disableTimestamps = false) {
1711
- const startOfTextToken = this.tokenConfig.startOfTextToken
1532
+ const startOfTextToken = this.specialTokens.startOfTextToken
1712
1533
 
1713
1534
  let tokens: number[]
1714
1535
 
1715
1536
  if (this.isMultiligualModel) {
1716
- const languageToken = this.tokenConfig.languageTokensStart + languageIdLookup[language]
1717
- const taskToken = task === 'translate' ? this.tokenConfig.translateTaskToken : this.tokenConfig.transcribeTaskToken
1537
+ const languageToken = this.specialTokens.languageTokensStart + languageIdLookup[language]
1538
+ const taskToken = task === 'translate' ? this.specialTokens.translateTaskToken : this.specialTokens.transcribeTaskToken
1718
1539
 
1719
1540
  tokens = [startOfTextToken, languageToken, taskToken]
1720
1541
  } else {
@@ -1722,7 +1543,7 @@ export class Whisper {
1722
1543
  }
1723
1544
 
1724
1545
  if (disableTimestamps) {
1725
- tokens.push(this.tokenConfig.noTimestampsToken)
1546
+ tokens.push(this.specialTokens.noTimestampsToken)
1726
1547
  }
1727
1548
 
1728
1549
  return tokens
@@ -1732,6 +1553,18 @@ export class Whisper {
1732
1553
  return this.tokensToText([token], includeMetadataTokens)
1733
1554
  }
1734
1555
 
1556
+ tokenToBytes(token: number) {
1557
+ return this.tiktoken!.decode_single_token_bytes(token)
1558
+ }
1559
+
1560
+ tokensToBytes(tokens: number[]) {
1561
+ const tokensBytes = tokens.map(token => this.tokenToBytes(token))
1562
+
1563
+ const concatenatedTokensBytes = concatUint8Arrays(tokensBytes)
1564
+
1565
+ return concatenatedTokensBytes
1566
+ }
1567
+
1735
1568
  tokensToText(tokens: number[], includeMetadataTokens = false) {
1736
1569
  tokens.forEach(token => this.assertIsValidToken(token))
1737
1570
 
@@ -1743,13 +1576,13 @@ export class Whisper {
1743
1576
  return ''
1744
1577
  }
1745
1578
 
1746
- const decodedBytes = this.tiktoken!.decode(new Uint32Array(tokens))
1579
+ try {
1580
+ const decodedBytes = this.tiktoken!.decode(new Uint32Array(tokens))
1747
1581
 
1748
- const textDecoder = new TextDecoder()
1582
+ const textDecoder = new TextDecoder()
1749
1583
 
1750
- const decodedString = textDecoder.decode(decodedBytes)
1584
+ const decodedString = textDecoder.decode(decodedBytes)
1751
1585
 
1752
- try {
1753
1586
  return decodedString
1754
1587
  } catch {
1755
1588
  return '[TOKENIZER_FAILED]'
@@ -1761,23 +1594,23 @@ export class Whisper {
1761
1594
  }
1762
1595
 
1763
1596
  isTextToken(token: number) {
1764
- return token < this.tokenConfig.endOfTextToken
1597
+ return token < this.specialTokens.endOfTextToken
1765
1598
  }
1766
1599
 
1767
1600
  isMetadataToken(token: number) {
1768
- return token >= this.tokenConfig.endOfTextToken
1601
+ return token >= this.specialTokens.endOfTextToken
1769
1602
  }
1770
1603
 
1771
1604
  isLanguageToken(token: number) {
1772
- return token >= this.tokenConfig.languageTokensStart && token < this.tokenConfig.languageTokensEnd
1605
+ return token >= this.specialTokens.languageTokensStart && token < this.specialTokens.languageTokensEnd
1773
1606
  }
1774
1607
 
1775
1608
  isTimestampToken(token: number) {
1776
- return token >= this.tokenConfig.timestampTokensStart
1609
+ return token >= this.specialTokens.timestampTokensStart
1777
1610
  }
1778
1611
 
1779
1612
  isNonTimestampToken(token: number) {
1780
- return token < this.tokenConfig.timestampTokensStart
1613
+ return token < this.specialTokens.timestampTokensStart
1781
1614
  }
1782
1615
 
1783
1616
  timestampTokenToSeconds(timestampToken: number) {
@@ -1787,7 +1620,7 @@ export class Whisper {
1787
1620
  throw new Error(`Invalid timestamp token: ${timestampToken}`)
1788
1621
  }
1789
1622
 
1790
- let seconds = (timestampToken - this.tokenConfig.timestampTokensStart) * 0.02
1623
+ let seconds = (timestampToken - this.specialTokens.timestampTokensStart) * 0.02
1791
1624
  seconds = clip(seconds, 0.0, 30.0)
1792
1625
 
1793
1626
  return seconds
@@ -1795,7 +1628,7 @@ export class Whisper {
1795
1628
 
1796
1629
  isValidToken(token: number) {
1797
1630
  //return token < this.tokenConfig.timestampTokensEnd
1798
- return token <= this.tokenConfig.timestampTokensEnd
1631
+ return token <= this.specialTokens.timestampTokensEnd
1799
1632
  }
1800
1633
 
1801
1634
  assertIsValidToken(token: number) {
@@ -1821,7 +1654,7 @@ export class Whisper {
1821
1654
  throw new Error(`Invalid language token: ${languageToken}`)
1822
1655
  }
1823
1656
 
1824
- let languageIndex = languageToken - this.tokenConfig.languageTokensStart
1657
+ let languageIndex = languageToken - this.specialTokens.languageTokensStart
1825
1658
 
1826
1659
  if (this.isEnglishOnlyModel) {
1827
1660
  languageIndex += 1
@@ -1832,24 +1665,20 @@ export class Whisper {
1832
1665
  return this.isMultiligualModel === false
1833
1666
  }
1834
1667
 
1835
- get isLargeModel() {
1836
- return this.modelName.startsWith('large')
1837
- }
1838
-
1839
- get filterbankCount() {
1840
- return this.isLargeModel ? 128 : 80
1668
+ get isLargeV3Model() {
1669
+ return this.modelId.startsWith('large-v3')
1841
1670
  }
1842
1671
 
1843
- get filterbanks() {
1844
- return this.isLargeModel ? filterbanks_128 : filterbanks_80
1672
+ get melBandCount() {
1673
+ return this.isLargeV3Model ? 128 : 80
1845
1674
  }
1846
1675
 
1847
- get alignmentHeadIndexes() {
1848
- return alignmentHeadsIndexes[this.modelName]
1676
+ get melFilterbank() {
1677
+ return this.isLargeV3Model ? melFilterbank128 : melFilterbanks80
1849
1678
  }
1850
1679
 
1851
1680
  get defaultTimestampAccuracy() {
1852
- if (this.modelName.startsWith('tiny') || this.modelName.startsWith('base')) {
1681
+ if (this.modelId.startsWith('tiny') || this.modelId.startsWith('base')) {
1853
1682
  return 'high'
1854
1683
  } else {
1855
1684
  return 'medium'
@@ -1906,7 +1735,7 @@ export class Whisper {
1906
1735
  const wordTokenData: WhisperTokenData[] = []
1907
1736
  const nonWordTokenData: WhisperTokenData[] = []
1908
1737
 
1909
- for (let i = 0; i < this.tokenConfig.endOfTextToken; i++) {
1738
+ for (let i = 0; i < this.specialTokens.endOfTextToken; i++) {
1910
1739
  const tokenText = this.tokenToText(i, false)
1911
1740
 
1912
1741
  const isNonWordToken = /^[\s\p{Punctuation}\p{Symbol}]+$/u.test(tokenText)
@@ -1943,109 +1772,68 @@ export class Whisper {
1943
1772
  }
1944
1773
  }
1945
1774
 
1946
- export async function loadPackagesAndGetPaths(modelName: WhisperModelName | undefined, languageCode: string | undefined) {
1947
- if (modelName) {
1948
- modelName = normalizeWhisperModelName(modelName, languageCode)
1949
- } else {
1950
- if (languageCode) {
1951
- const shortLanguageCode = getShortLanguageCode(languageCode)
1775
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
1776
+ // Utility methods
1777
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
1952
1778
 
1953
- modelName = shortLanguageCode === 'en' ? 'tiny.en' : 'tiny'
1954
- } else {
1955
- modelName = 'tiny'
1956
- }
1957
- }
1958
-
1959
- if (modelName.startsWith('large') && modelName !== 'large-v3-turbo') {
1960
- throw new Error(`Models 'large-v1', 'large-v2', 'large-v3' are not currently supported by the integrated Whisper engine due to model size restrictions of onnxruntime-node. To use large models, you can either select large-v3-turbo use the whisper.cpp engine instead.`)
1961
- }
1962
-
1963
- const packageName = modelNameToPackageName[modelName]
1964
-
1965
- const modelDir = await loadPackage(packageName)
1966
-
1967
- return { modelName, modelDir }
1779
+ export function isMultilingualModel(modelId: WhisperModelId) {
1780
+ return !isEnglishOnlyModel(modelId)
1968
1781
  }
1969
1782
 
1970
- export function normalizeWhisperModelName(modelName: WhisperModelName, languageCode: string | undefined): WhisperModelName {
1971
- if (languageCode !== 'en' && modelName.endsWith('.en')) {
1972
- const originalModelName = modelName
1973
- modelName = modelName.slice(0, modelName.length - 3) as WhisperModelName
1974
-
1975
- const logger = new Logger()
1976
- logger.logTitledMessage(`Warning`, `The model '${originalModelName}' is English only and cannot be used to transcribe language '${languageCode}'. Using '${modelName}' instead.`, chalk.yellowBright, 'warning')
1977
- }
1978
-
1979
- return modelName
1783
+ export function isEnglishOnlyModel(modelId: WhisperModelId) {
1784
+ return modelId.endsWith('.en')
1980
1785
  }
1981
1786
 
1982
- export function isMultilingualModel(modelName: WhisperModelName) {
1983
- return !isEnglishOnlyModel(modelName)
1984
- }
1787
+ export async function loadLibraryPackages(enableGPU: boolean | undefined, callbacks: LoadPackageCallbacks) {
1788
+ const arch = process.arch
1789
+ const platform = process.platform
1985
1790
 
1986
- export function isEnglishOnlyModel(modelName: WhisperModelName) {
1987
- return modelName.endsWith('.en')
1988
- }
1791
+ let packageName: string
1989
1792
 
1990
- export function getDefaultEncoderProvidersForModel(modelName: WhisperModelName): OnnxExecutionProvider[] {
1991
- if (dmlProviderAvailable()) {
1992
- return ['dml', 'cpu']
1993
- } else {
1994
- return []
1995
- }
1996
- }
1793
+ const useMacOsGpuBuildByDefault = true
1997
1794
 
1998
- export function getDefaultDecoderProvidersForModel(modelName: WhisperModelName): OnnxExecutionProvider[] {
1999
- if (modelName.startsWith('small') || modelName.startsWith('medium') || modelName.startsWith('large')) {
2000
- if (dmlProviderAvailable()) {
2001
- return ['dml', 'cpu']
1795
+ if (platform === 'win32' && arch === 'x64') {
1796
+ if (enableGPU) {
1797
+ packageName = 'whisper.cpp-lib-windows-x64-cublas-12.4.0'
2002
1798
  } else {
2003
- return []
1799
+ packageName = 'whisper.cpp-lib-windows-x64-cpu'
2004
1800
  }
1801
+ } else if (platform === 'win32' && arch === 'arm64') {
1802
+ packageName = 'whisper.cpp-lib-windows-arm64-cpu'
1803
+ } else if (platform === 'darwin' && arch === 'x64') {
1804
+ if (useMacOsGpuBuildByDefault || enableGPU) {
1805
+ packageName = 'whisper.cpp-lib-macos-universal'
1806
+ } else {
1807
+ packageName = 'whisper.cpp-lib-macos-universal-cpu-basic'
1808
+ }
1809
+ } else if (platform === 'darwin' && arch === 'arm64') {
1810
+ if (useMacOsGpuBuildByDefault || enableGPU) {
1811
+ packageName = 'whisper.cpp-lib-macos-universal'
1812
+ } else {
1813
+ packageName = 'whisper.cpp-lib-macos-universal-cpu-basic'
1814
+ }
1815
+ } else if (platform === 'linux' && arch === 'x64') {
1816
+ if (enableGPU) {
1817
+ packageName = 'whisper.cpp-lib-linux-x64-cublas-12.4.0'
1818
+ } else {
1819
+ packageName = 'whisper.cpp-lib-linux-x64-cpu'
1820
+ }
1821
+ } else if (platform === 'linux' && arch === 'arm64') {
1822
+ packageName = 'whisper.cpp-lib-linux-arm64-cpu'
2005
1823
  } else {
2006
- return []
1824
+ throw new Error(`The platform and architecture combination ${platform} / ${arch} is not supported by the Whisper engine`)
2007
1825
  }
2008
- }
2009
1826
 
2010
- export type WhisperTokenData = {
2011
- id: number
2012
- text: string
2013
- }
1827
+ const whisperCppLibPath = await loadPackage(packageName, callbacks)
1828
+
1829
+ const libPath = whisperCppLibPath
2014
1830
 
2015
- export type WhisperLogitFilter = (logits: number[], decodedTokens: number[], isFirstPart: boolean, isFinalPart: boolean) => number[]
2016
-
2017
- export type WhisperModelName =
2018
- 'tiny' |
2019
- 'tiny.en' |
2020
- 'base' |
2021
- 'base.en' |
2022
- 'small' |
2023
- 'small.en' |
2024
- 'medium' |
2025
- 'medium.en' |
2026
- 'large-v1' |
2027
- 'large-v2' |
2028
- 'large-v3' |
2029
- 'large-v3-turbo'
2030
-
2031
- export type WhisperTask = 'transcribe' | 'translate' | 'detect-language'
2032
-
2033
- export const modelNameToPackageName: { [modelName in WhisperModelName]: string } = {
2034
- 'tiny': 'whisper-tiny',
2035
- 'tiny.en': 'whisper-tiny.en',
2036
- 'base': 'whisper-base',
2037
- 'base.en': 'whisper-base.en',
2038
- 'small': 'whisper-small',
2039
- 'small.en': 'whisper-small.en',
2040
- 'medium': 'whisper-medium',
2041
- 'medium.en': 'whisper-medium.en',
2042
- 'large-v1': 'whisper-large-v1',
2043
- 'large-v2': 'whisper-large-v2',
2044
- 'large-v3': 'whisper-large-v3',
2045
- 'large-v3-turbo': 'whisper-large-v3-turbo-fp16',
1831
+ return { libPath }
2046
1832
  }
2047
1833
 
2048
- export const tokenizerPackageName = 'whisper-tokenizer'
1834
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////
1835
+ // Tables
1836
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////
2049
1837
 
2050
1838
  const languageIdLookup: { [s: string]: number } = {
2051
1839
  'en': 0,
@@ -2150,7 +1938,7 @@ const languageIdLookup: { [s: string]: number } = {
2150
1938
  //'yue': 99
2151
1939
  }
2152
1940
 
2153
- const alignmentHeadsIndexes: { [name in WhisperModelName]: number[] } = {
1941
+ const alignmentHeadsIndices: { [name in WhisperModelId]: number[] } = {
2154
1942
  'tiny': [14, 18, 20, 21, 22, 23,],
2155
1943
  'tiny.en': [6, 12, 17, 18, 19, 20, 21, 22,],
2156
1944
  'base': [25, 34, 35, 39, 41, 42, 44, 46,],
@@ -2165,7 +1953,7 @@ const alignmentHeadsIndexes: { [name in WhisperModelName]: number[] } = {
2165
1953
  'large-v3-turbo': [44, 51, 63, 66, 71, 74,],
2166
1954
  }
2167
1955
 
2168
- const filterbanks_80: Filterbank[] = [
1956
+ const melFilterbanks80: Filterbank = [
2169
1957
  /* 0 */ { startIndex: 1, weights: [0.02486259490251541,] },
2170
1958
 
2171
1959
  /* 1 */ { startIndex: 1, weights: [0.001990821911022067, 0.022871771827340126,] },
@@ -2327,7 +2115,7 @@ const filterbanks_80: Filterbank[] = [
2327
2115
  /* 79 */ { startIndex: 186, weights: [0.000366741674952209, 0.0008330700220540166, 0.0012993983691558242, 0.0017657268326729536, 0.0022320549469441175, 0.002698383294045925, 0.0031647118739783764, 0.003141313325613737, 0.002692554146051407, 0.0022437951993197203, 0.00179503601975739, 0.0013462770730257034, 0.000897518009878695, 0.0004487590049393475,] },
2328
2116
  ]
2329
2117
 
2330
- const filterbanks_128: Filterbank[] = [
2118
+ const melFilterbank128: Filterbank = [
2331
2119
  /* 0 */ { startIndex: 1, weights: [0.012373986653983593,] },
2332
2120
 
2333
2121
  /* 1 */ { startIndex: 1, weights: [0.030392564833164215,] },
@@ -2585,9 +2373,75 @@ const filterbanks_128: Filterbank[] = [
2585
2373
  /* 127 */ { startIndex: 191, weights: [0.0004756950947921723, 0.0016171716852113605, 0.002758648479357362, 0.0039001251570880413, 0.005041601601988077, 0.004457120783627033, 0.003342840587720275, 0.0022285603918135166, 0.0011142801959067583,] },
2586
2374
  ]
2587
2375
 
2376
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
2377
+ // Types and constants
2378
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
2379
+ interface WhisperSpecialTokens {
2380
+ endOfTextToken: number
2381
+ startOfTextToken: number
2382
+
2383
+ languageTokensStart: number
2384
+ languageTokensEnd: number
2385
+
2386
+ translateTaskToken: number
2387
+ transcribeTaskToken: number
2388
+ startOfPromptToken: number
2389
+ nonSpeechToken: number
2390
+ noTimestampsToken: number
2391
+
2392
+ timestampTokensStart: number
2393
+ timestampTokensEnd: number
2394
+ }
2395
+
2396
+ const multilingualSpecialTokens: WhisperSpecialTokens = {
2397
+ endOfTextToken: 50257,
2398
+ startOfTextToken: 50258,
2399
+
2400
+ languageTokensStart: 50259,
2401
+ languageTokensEnd: 50358,
2402
+
2403
+ translateTaskToken: 50358,
2404
+ transcribeTaskToken: 50359,
2405
+ startOfPromptToken: 50361,
2406
+ nonSpeechToken: 50362,
2407
+ noTimestampsToken: 50363,
2408
+
2409
+ timestampTokensStart: 50364,
2410
+ timestampTokensEnd: 50364 + 1501,
2411
+ }
2412
+
2413
+ const englishOnlySpecialTokens: WhisperSpecialTokens = {
2414
+ endOfTextToken: 50256,
2415
+ startOfTextToken: 50257,
2416
+
2417
+ languageTokensStart: 50258,
2418
+ languageTokensEnd: 50358,
2419
+
2420
+ translateTaskToken: 50358,
2421
+ transcribeTaskToken: 50359,
2422
+ startOfPromptToken: 50360,
2423
+ nonSpeechToken: 50361,
2424
+ noTimestampsToken: 50362,
2425
+
2426
+ timestampTokensStart: 50363,
2427
+ timestampTokensEnd: 50363 + 1501,
2428
+ }
2429
+
2430
+ export type WhisperLogitFilter = (logits: Float32Array, decodedTokens: number[], isFirstPart: boolean, isFinalPart: boolean) => Float32Array
2431
+
2432
+ export type WhisperTokenData = {
2433
+ id: number
2434
+ text: string
2435
+ }
2436
+
2588
2437
  // Recognition options
2589
2438
  export interface WhisperOptions {
2590
- model?: WhisperModelName
2439
+ model?: WhisperQuantizedModelId
2440
+
2441
+ enableGPU?: boolean
2442
+ threadCount?: number
2443
+ gpuDeviceIndex?: number
2444
+
2591
2445
  temperature?: number
2592
2446
  prompt?: string
2593
2447
  topCandidateCount?: number
@@ -2600,8 +2454,6 @@ export interface WhisperOptions {
2600
2454
  endTokenThreshold?: number
2601
2455
  includeEndTokenInCandidates?: boolean
2602
2456
  timestampAccuracy?: WhisperTimestampAccuracy,
2603
- encoderProvider?: OnnxExecutionProvider
2604
- decoderProvider?: OnnxExecutionProvider
2605
2457
  seed?: number
2606
2458
  }
2607
2459
 
@@ -2609,6 +2461,11 @@ const largestMaximumTokensPerPart = 220
2609
2461
 
2610
2462
  export const defaultWhisperOptions: WhisperOptions = {
2611
2463
  model: undefined,
2464
+
2465
+ enableGPU: false,
2466
+ threadCount: 4,
2467
+ gpuDeviceIndex: 0,
2468
+
2612
2469
  temperature: 0.1,
2613
2470
  prompt: undefined,
2614
2471
  topCandidateCount: 5,
@@ -2621,62 +2478,56 @@ export const defaultWhisperOptions: WhisperOptions = {
2621
2478
  endTokenThreshold: 0.9,
2622
2479
  includeEndTokenInCandidates: true,
2623
2480
  timestampAccuracy: undefined,
2624
- encoderProvider: undefined,
2625
- decoderProvider: undefined,
2626
2481
  seed: undefined,
2627
2482
  }
2628
2483
 
2629
2484
  // Alignment options
2630
2485
  export interface WhisperAlignmentOptions {
2631
- model?: WhisperModelName
2486
+ model?: WhisperQuantizedModelId
2487
+
2488
+ enableGPU?: boolean
2489
+ threadCount?: number
2490
+ gpuDeviceIndex?: number
2491
+
2632
2492
  endTokenThreshold?: number
2633
2493
  maxTokensPerPart?: number
2634
2494
  timestampAccuracy?: WhisperTimestampAccuracy,
2635
-
2636
- encoderProvider?: OnnxExecutionProvider
2637
- decoderProvider?: OnnxExecutionProvider
2638
2495
  }
2639
2496
 
2640
2497
  export const defaultWhisperAlignmentOptions: WhisperAlignmentOptions = {
2641
2498
  model: undefined,
2499
+
2500
+ enableGPU: false,
2501
+ threadCount: 4,
2502
+ gpuDeviceIndex: 0,
2503
+
2642
2504
  endTokenThreshold: 0.9,
2643
2505
  maxTokensPerPart: largestMaximumTokensPerPart,
2644
2506
  timestampAccuracy: undefined,
2645
-
2646
- encoderProvider: undefined,
2647
- decoderProvider: undefined,
2648
2507
  }
2649
2508
 
2650
2509
  // Language detection options
2651
2510
  export interface WhisperLanguageDetectionOptions {
2652
- model?: WhisperModelName
2511
+ model?: WhisperQuantizedModelId
2512
+
2513
+ enableGPU?: boolean
2514
+ threadCount?: number
2515
+ gpuDeviceIndex?: number
2516
+
2653
2517
  temperature?: number
2654
- encoderProvider?: OnnxExecutionProvider
2655
- decoderProvider?: OnnxExecutionProvider
2656
2518
  }
2657
2519
 
2658
2520
  export const defaultWhisperLanguageDetectionOptions: WhisperLanguageDetectionOptions = {
2659
2521
  model: undefined,
2660
- temperature: 1.0,
2661
- encoderProvider: undefined,
2662
- decoderProvider: undefined,
2663
- }
2664
2522
 
2665
- // Voice activity detection options
2666
- export interface WhisperVADOptions {
2667
- model?: WhisperModelName
2668
- temperature?: number
2669
- encoderProvider?: OnnxExecutionProvider
2670
- decoderProvider?: OnnxExecutionProvider
2671
- }
2523
+ enableGPU: false,
2524
+ threadCount: 4,
2525
+ gpuDeviceIndex: 0,
2672
2526
 
2673
- export const defaultWhisperVADOptions: WhisperVADOptions = {
2674
- model: undefined,
2675
2527
  temperature: 1.0,
2676
- encoderProvider: undefined,
2677
- decoderProvider: undefined,
2678
2528
  }
2679
2529
 
2680
2530
  export type WhisperTimestampAccuracy = 'medium' | 'high'
2681
2531
 
2682
- export type WhisperPartCallback = (partTranscript: string, partTokenTimeline: Timeline, partWordTimeline: Timeline) => void
2532
+ export type WhisperPartCallback = (partTranscript: string, partTokenTimeline: Timeline, partWordTimeline: Timeline) => Promise<void>
2533
+ export type WhisperTokenCallback = (tokenId: number, tokenUtf8Bytes: Uint8Array) => Promise<void>