echogarden 1.3.0 → 1.3.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 (62) hide show
  1. package/dist/alignment/TextAlignment.d.ts +16 -0
  2. package/dist/alignment/TextAlignment.js +63 -0
  3. package/dist/alignment/TextAlignment.js.map +1 -0
  4. package/dist/api/Alignment.js +6 -1
  5. package/dist/api/Alignment.js.map +1 -1
  6. package/dist/api/Denoising.js +0 -1
  7. package/dist/api/Denoising.js.map +1 -1
  8. package/dist/api/LanguageDetection.js +2 -2
  9. package/dist/api/LanguageDetection.js.map +1 -1
  10. package/dist/api/Recognition.js +3 -1
  11. package/dist/api/Recognition.js.map +1 -1
  12. package/dist/api/Synthesis.js +1 -1
  13. package/dist/api/Synthesis.js.map +1 -1
  14. package/dist/api/Translation.js +3 -1
  15. package/dist/api/Translation.js.map +1 -1
  16. package/dist/api/TranslationAlignment.js +4 -1
  17. package/dist/api/TranslationAlignment.js.map +1 -1
  18. package/dist/api/VoiceActivityDetection.js +5 -2
  19. package/dist/api/VoiceActivityDetection.js.map +1 -1
  20. package/dist/audio/AudioUtilities.d.ts +1 -1
  21. package/dist/audio/AudioUtilities.js +2 -2
  22. package/dist/audio/AudioUtilities.js.map +1 -1
  23. package/dist/cli/CLILauncher.js +1 -2
  24. package/dist/cli/CLILauncher.js.map +1 -1
  25. package/dist/codecs/FFMpegTranscoder.js +4 -1
  26. package/dist/codecs/FFMpegTranscoder.js.map +1 -1
  27. package/dist/codecs/WaveCodec.d.ts +1 -1
  28. package/dist/codecs/WaveCodec.js +13 -5
  29. package/dist/codecs/WaveCodec.js.map +1 -1
  30. package/dist/dsp/SpeexResampler.js +49 -21
  31. package/dist/dsp/SpeexResampler.js.map +1 -1
  32. package/dist/synthesis/CoquiServerTTS.js +1 -1
  33. package/dist/synthesis/CoquiServerTTS.js.map +1 -1
  34. package/dist/synthesis/VitsTTS.js +1 -1
  35. package/dist/synthesis/VitsTTS.js.map +1 -1
  36. package/dist/text-translation/NLLBTextTranslation.d.ts +1 -0
  37. package/dist/text-translation/NLLBTextTranslation.js +237 -0
  38. package/dist/text-translation/NLLBTextTranslation.js.map +1 -0
  39. package/dist/utilities/PackageManager.js +2 -0
  40. package/dist/utilities/PackageManager.js.map +1 -1
  41. package/docs/Development.md +1 -1
  42. package/docs/Server.md +1 -1
  43. package/docs/Tasklist.md +1 -1
  44. package/package.json +11 -10
  45. package/src/alignment/TextAlignment.ts +96 -0
  46. package/src/api/Alignment.ts +10 -1
  47. package/src/api/Denoising.ts +0 -2
  48. package/src/api/LanguageDetection.ts +2 -3
  49. package/src/api/Recognition.ts +3 -1
  50. package/src/api/Synthesis.ts +1 -1
  51. package/src/api/Translation.ts +3 -1
  52. package/src/api/TranslationAlignment.ts +5 -1
  53. package/src/api/VoiceActivityDetection.ts +7 -4
  54. package/src/audio/AudioUtilities.ts +2 -2
  55. package/src/cli/CLILauncher.ts +1 -2
  56. package/src/codecs/FFMpegTranscoder.ts +5 -1
  57. package/src/codecs/WaveCodec.ts +15 -5
  58. package/src/dsp/SpeexResampler.ts +62 -23
  59. package/src/synthesis/CoquiServerTTS.ts +1 -1
  60. package/src/synthesis/VitsTTS.ts +2 -2
  61. package/src/text-translation/NLLBTextTranslation.ts +252 -0
  62. package/src/utilities/PackageManager.ts +3 -0
@@ -20,16 +20,15 @@ export async function detectVoiceActivity(input: AudioSourceParam, options: VADO
20
20
 
21
21
  const startTimestamp = logger.getTimestamp()
22
22
 
23
- logger.start('Prepare for voice activity detection')
24
-
25
23
  const inputRawAudio = await ensureRawAudio(input)
26
24
 
25
+ logger.start(`Resample audio to 16kHz mono`)
27
26
  let sourceRawAudio = await ensureRawAudio(inputRawAudio, 16000, 1)
28
27
 
29
- options = extendDeep(defaultVADOptions, options)
30
-
31
28
  logger.start(`Detect voice activity with ${options.engine}`)
32
29
 
30
+ options = extendDeep(defaultVADOptions, options)
31
+
33
32
  const activityThreshold = options.activityThreshold!
34
33
 
35
34
  let verboseTimeline: Timeline
@@ -224,6 +223,10 @@ export function convertCroppedToUncroppedTimeline(timeline: Timeline, uncropTime
224
223
  function mapUsingUncropTimeline(startTimeInCroppedAudio: number, endTimeInCroppedAudio: number, uncropTimeline: Timeline) {
225
224
  let offsetInCroppedAudio = 0
226
225
 
226
+ if (endTimeInCroppedAudio < startTimeInCroppedAudio) {
227
+ endTimeInCroppedAudio = startTimeInCroppedAudio
228
+ }
229
+
227
230
  let bestOverlapDuration = -1
228
231
  let mappedStartTime = -1
229
232
  let mappedEndTime = -1
@@ -11,8 +11,8 @@ export function encodeRawAudioToWave(rawAudio: RawAudio, bitDepth: BitDepth = 16
11
11
  return encodeWave(rawAudio, bitDepth, sampleFormat, speakerPositionMask)
12
12
  }
13
13
 
14
- export function decodeWaveToRawAudio(waveFileBuffer: Buffer, ignoreTruncatedChunks = false) {
15
- return decodeWave(waveFileBuffer, ignoreTruncatedChunks)
14
+ export function decodeWaveToRawAudio(waveFileBuffer: Buffer, ignoreTruncatedChunks = true, ignoreOverflowingDataChunks = true) {
15
+ return decodeWave(waveFileBuffer, ignoreTruncatedChunks, ignoreOverflowingDataChunks)
16
16
  }
17
17
 
18
18
  ////////////////////////////////////////////////////////////////////////////////////////////////
@@ -12,9 +12,8 @@ const scriptArgs = process.argv.slice(2)
12
12
  const cliScriptPath = resolveToModuleRootDir('dist/cli/CLIStarter.js')
13
13
 
14
14
  const args = [
15
- '--no-warnings',
16
- '--no-experimental-fetch',
17
15
  '--experimental-wasi-unstable-preview1',
16
+ '--no-warnings',
18
17
  cliScriptPath,
19
18
  ...scriptArgs
20
19
  ]
@@ -37,7 +37,11 @@ export async function decodeToChannels(input: string | Buffer, outSampleRate?: n
37
37
 
38
38
  const waveAudio = await transcode(input, outputOptions)
39
39
 
40
- const { rawAudio } = decodeWaveToRawAudio(waveAudio, true)
40
+ const logger = new Logger()
41
+
42
+ logger.start(`Convert wave buffer to raw audio`)
43
+ const { rawAudio } = decodeWaveToRawAudio(waveAudio)
44
+ logger.end()
41
45
 
42
46
  return rawAudio
43
47
  }
@@ -18,18 +18,20 @@ export function encodeWave(rawAudio: RawAudio, bitDepth: BitDepth = 16, sampleFo
18
18
 
19
19
  const dataSubChunkBuffer = Buffer.alloc(4 + 4 + audioDataLength)
20
20
  dataSubChunkBuffer.write('data', 0, 'ascii')
21
- dataSubChunkBuffer.writeUint32LE(audioDataLength, 4)
21
+ const dataChunkLength = Math.min(audioDataLength, 4294967295) // Ensure large data chunk length is clipped to max
22
+ dataSubChunkBuffer.writeUint32LE(dataChunkLength, 4)
22
23
  dataSubChunkBuffer.set(audioBuffer, 8)
23
24
 
24
25
  const riffChunkHeaderBuffer = Buffer.alloc(12)
25
26
  riffChunkHeaderBuffer.write('RIFF', 0, 'ascii')
26
- riffChunkHeaderBuffer.writeUint32LE(4 + formatSubChunkBuffer.length + dataSubChunkBuffer.length, 4)
27
+ const riffChunkLength = Math.min(4 + formatSubChunkBuffer.length + dataSubChunkBuffer.length, 4294967295) // Ensure large RIFF chunk length is clipped to max
28
+ riffChunkHeaderBuffer.writeUint32LE(riffChunkLength, 4)
27
29
  riffChunkHeaderBuffer.write('WAVE', 8, 'ascii')
28
30
 
29
31
  return Buffer.concat([riffChunkHeaderBuffer, formatSubChunkBuffer, dataSubChunkBuffer])
30
32
  }
31
33
 
32
- export function decodeWave(waveData: Buffer, ignoreTruncatedChunks = false) {
34
+ export function decodeWave(waveData: Buffer, ignoreTruncatedChunks = true, ignoreOverflowingDataChunks = true) {
33
35
  let readOffset = 0
34
36
 
35
37
  const riffId = waveData.subarray(readOffset, readOffset + 4).toString('ascii')
@@ -40,7 +42,7 @@ export function decodeWave(waveData: Buffer, ignoreTruncatedChunks = false) {
40
42
 
41
43
  readOffset += 4
42
44
 
43
- const riffChunkSize = waveData.readUInt32LE(readOffset)
45
+ let riffChunkSize = waveData.readUInt32LE(readOffset)
44
46
 
45
47
  readOffset += 4
46
48
 
@@ -50,6 +52,10 @@ export function decodeWave(waveData: Buffer, ignoreTruncatedChunks = false) {
50
52
  throw new Error('Not a valid wave file. No WAVE id found at offset 8.')
51
53
  }
52
54
 
55
+ if (ignoreOverflowingDataChunks && riffChunkSize === 4294967295) {
56
+ riffChunkSize = waveData.length - 8
57
+ }
58
+
53
59
  if (riffChunkSize < waveData.length - 8) {
54
60
  throw new Error(`RIFF chunk length ${riffChunkSize} is smaller than the remaining size of the buffer (${waveData.length - 8})`)
55
61
  }
@@ -67,7 +73,7 @@ export function decodeWave(waveData: Buffer, ignoreTruncatedChunks = false) {
67
73
  const subChunkIdentifier = waveData.subarray(readOffset, readOffset + 4).toString('ascii')
68
74
  readOffset += 4
69
75
 
70
- const subChunkSize = waveData.readUInt32LE(readOffset)
76
+ let subChunkSize = waveData.readUInt32LE(readOffset)
71
77
  readOffset += 4
72
78
 
73
79
  if (!ignoreTruncatedChunks && subChunkSize > waveData.length - readOffset) {
@@ -81,6 +87,10 @@ export function decodeWave(waveData: Buffer, ignoreTruncatedChunks = false) {
81
87
  throw new Error('A data subchunk was encountered before a format subchunk')
82
88
  }
83
89
 
90
+ if (ignoreOverflowingDataChunks && subChunkSize === 4294967295) {
91
+ subChunkSize = waveData.length - readOffset
92
+ }
93
+
84
94
  // If the data chunk is truncated, but truncations are ignored,
85
95
  // it would be read up to the end of the buffer
86
96
  dataBuffers.push(waveData.subarray(readOffset, readOffset + subChunkSize))
@@ -1,16 +1,27 @@
1
1
  import { RawAudio, cloneRawAudio } from '../audio/AudioUtilities.js'
2
+ import { concatFloat32Arrays } from '../utilities/Utilities.js'
2
3
  import { WasmMemoryManager } from '../utilities/WasmMemoryManager.js'
3
4
 
4
5
  let speexResamplerInstance: any
5
6
 
6
- export async function resampleAudioSpeex(rawAudio: RawAudio, outSampleRate: number, quality = 0) {
7
+ export async function resampleAudioSpeex(rawAudio: RawAudio, outSampleRate: number, quality = 0): Promise<RawAudio> {
7
8
  const channelCount = rawAudio.audioChannels.length
8
9
  const inSampleRate = rawAudio.sampleRate
9
10
 
10
- if (inSampleRate == outSampleRate) {
11
+ const totalSampleCount = rawAudio.audioChannels[0].length
12
+ const sampleRateRatio = outSampleRate / inSampleRate
13
+
14
+ if (inSampleRate === outSampleRate) {
11
15
  return cloneRawAudio(rawAudio)
12
16
  }
13
17
 
18
+ if (totalSampleCount === 0) {
19
+ return {
20
+ ...cloneRawAudio(rawAudio),
21
+ sampleRate: outSampleRate
22
+ } as RawAudio
23
+ }
24
+
14
25
  const m = await getSpeexResamplerInstance()
15
26
  const wasmMemory = new WasmMemoryManager(m)
16
27
 
@@ -23,48 +34,76 @@ export async function resampleAudioSpeex(rawAudio: RawAudio, outSampleRate: numb
23
34
  }
24
35
 
25
36
  const initErrRef = wasmMemory.allocInt32()
26
- const resamplerState = m._speex_resampler_init(channelCount, inSampleRate, outSampleRate, quality, initErrRef.address)
37
+ const resamplerStateAddress = m._speex_resampler_init(channelCount, inSampleRate, outSampleRate, quality, initErrRef.address)
27
38
  let resultCode = initErrRef.value
28
39
 
29
40
  if (resultCode != 0) {
30
41
  throw new Error(`Speex resampler failed while initializing with code ${resultCode}: ${speexResultCodeToString(resultCode)}`)
31
42
  }
32
43
 
33
- const resampledAudio: RawAudio = { audioChannels: [], sampleRate: outSampleRate }
44
+ const inputLatency = m._speex_resampler_get_input_latency(resamplerStateAddress)
45
+ const outputLatency = m._speex_resampler_get_output_latency(resamplerStateAddress)
46
+
47
+ const maxChunkSize = 2 ** 20
48
+
49
+ const inputChunkSampleCountRef = wasmMemory.allocInt32()
50
+ const outputChunkSampleCountRef = wasmMemory.allocInt32()
51
+
52
+ const inputChunkSamplesRef = wasmMemory.allocFloat32Array(maxChunkSize * 2)
53
+ const outputChunkSamplesRef = wasmMemory.allocFloat32Array(Math.floor(maxChunkSize * sampleRateRatio) * 2)
54
+
55
+ const resampledAudioChunksForChannels: Float32Array[][] = []
56
+
57
+ for (let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
58
+ resampledAudioChunksForChannels.push([])
59
+ }
34
60
 
35
61
  for (let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
36
- const channelSamples = rawAudio.audioChannels[channelIndex]
62
+ for (let readOffset = 0; readOffset < totalSampleCount;) {
63
+ const isLastChunk = readOffset + maxChunkSize >= totalSampleCount
37
64
 
38
- const inputLatency = m._speex_resampler_get_input_latency(resamplerState)
39
- const outputLatency = m._speex_resampler_get_output_latency(resamplerState)
65
+ const inputPaddingSize = isLastChunk ? inputLatency : 0
66
+ const maxSamplesToRead = Math.min(maxChunkSize, totalSampleCount - readOffset) + inputPaddingSize
40
67
 
41
- const inSampleCount = channelSamples.length
42
- const inSampleCountRef = wasmMemory.allocInt32()
43
- inSampleCountRef.value = inSampleCount + inputLatency
68
+ const maxSamplesToWrite = outputChunkSamplesRef.length
44
69
 
45
- const inSamplesRef = wasmMemory.allocFloat32Array(inSampleCountRef.value)
46
- inSamplesRef.view.set(channelSamples)
70
+ const inputChunkSamplesForChannel = rawAudio.audioChannels[channelIndex].slice(readOffset, readOffset + maxSamplesToRead)
47
71
 
48
- const outSampleCount = Math.floor((inSampleCount / inSampleRate) * outSampleRate)
49
- const outSampleCountRef = wasmMemory.allocInt32()
50
- outSampleCountRef.value = outSampleCount + outputLatency
72
+ inputChunkSampleCountRef.value = maxSamplesToRead
73
+ outputChunkSampleCountRef.value = maxSamplesToWrite
51
74
 
52
- const outSamplesRef = wasmMemory.allocFloat32Array(outSampleCountRef.value)
75
+ inputChunkSamplesRef.view.set(inputChunkSamplesForChannel)
76
+ resultCode = m._speex_resampler_process_float(resamplerStateAddress, channelIndex, inputChunkSamplesRef.address, inputChunkSampleCountRef.address, outputChunkSamplesRef.address, outputChunkSampleCountRef.address)
53
77
 
54
- resultCode = m._speex_resampler_process_float(resamplerState, channelIndex, inSamplesRef.address, inSampleCountRef.address, outSamplesRef.address, outSampleCountRef.address)
78
+ if (resultCode != 0) {
79
+ throw new Error(`Speex resampler failed while resampling with code ${resultCode}: ${speexResultCodeToString(resultCode)}`)
80
+ }
55
81
 
56
- if (resultCode != 0) {
57
- throw new Error(`Speex resampler failed while resampling with code ${resultCode}: ${speexResultCodeToString(resultCode)}`)
58
- }
82
+ const samplesReadCount = inputChunkSampleCountRef.value
83
+ const samplesWrittenCount = outputChunkSampleCountRef.value
84
+
85
+ const resampledChannelAudio = outputChunkSamplesRef.view.slice(0, samplesWrittenCount)
59
86
 
60
- const resampledChannelAudio = outSamplesRef.view.slice(outputLatency)
87
+ resampledAudioChunksForChannels[channelIndex].push(resampledChannelAudio)
61
88
 
62
- resampledAudio.audioChannels.push(resampledChannelAudio)
89
+ readOffset += samplesReadCount
90
+ }
63
91
  }
64
92
 
65
- m._speex_resampler_destroy(resamplerState)
93
+ m._speex_resampler_destroy(resamplerStateAddress)
66
94
  wasmMemory.freeAll()
67
95
 
96
+ const resampledAudio: RawAudio = {
97
+ audioChannels: [],
98
+ sampleRate: outSampleRate
99
+ }
100
+
101
+ for (let i = 0; i < channelCount; i++) {
102
+ resampledAudioChunksForChannels[i][0] = resampledAudioChunksForChannels[i][0].slice(outputLatency)
103
+
104
+ resampledAudio.audioChannels.push(concatFloat32Arrays(resampledAudioChunksForChannels[i]))
105
+ }
106
+
68
107
  return resampledAudio
69
108
  }
70
109
 
@@ -21,7 +21,7 @@ export async function synthesize(text: string, speakerId: string | null, serverU
21
21
 
22
22
  const waveData = Buffer.from(response.data)
23
23
 
24
- const rawAudio = decodeWaveToRawAudio(waveData).rawAudio
24
+ const { rawAudio } = decodeWaveToRawAudio(waveData)
25
25
 
26
26
  logger.end()
27
27
 
@@ -52,7 +52,7 @@ export class VitsTTS {
52
52
 
53
53
  await this.initializeIfNeeded()
54
54
 
55
- await logger.startAsync('Prepare for synthesis')
55
+ await logger.startAsync('Prepare for VITS synthesis')
56
56
 
57
57
  const metadata = this.metadata
58
58
  const phonemeMap = this.phonemeMap!
@@ -78,7 +78,7 @@ export class VitsTTS {
78
78
  voice: espeakVoice,
79
79
  useKlatt: false
80
80
  }
81
-
81
+
82
82
  const { referenceSynthesizedAudio, referenceTimeline, fragments, phonemizedFragmentsSubstitutions, phonemizedSentence } = await Espeak.preprocessAndSynthesize(sentence, languageCode, espeakOptions, lexicons)
83
83
 
84
84
  if (phonemizedSentence.length == 0) {
@@ -0,0 +1,252 @@
1
+ import { Logger } from '../utilities/Logger.js'
2
+ import { loadPackage } from '../utilities/PackageManager.js'
3
+
4
+ export async function translateText(sourceText: string, sourceLanguage: string, targetLanguage: string) {
5
+ const logger = new Logger()
6
+
7
+ const { AutoTokenizer, M2M100ForConditionalGeneration } = await import('@echogarden/transformers-nodejs-lite')
8
+
9
+ const modelPath = await loadPackage(`xenova-nllb-200-distilled-600M-quantized`)
10
+
11
+ const tokenizer = await AutoTokenizer.from_pretrained(modelPath)
12
+ const model = await M2M100ForConditionalGeneration.from_pretrained(modelPath)
13
+
14
+ const config = {
15
+ src_lang: 'eng_Latn',
16
+ tgt_lang: 'fra_Latn'
17
+ }
18
+
19
+ const inputs = (tokenizer as any)._build_translation_inputs(sourceText, {
20
+ padding: true,
21
+ truncation: true,
22
+ }, config)
23
+
24
+ const result = await model.generate(inputs.input_ids, config)
25
+
26
+ logger.log(tokenizer.model.convert_ids_to_tokens(result[0]))
27
+
28
+ const inputTokens = tokenizer.model.convert_ids_to_tokens(Array.from(inputs.input_ids.data))
29
+ const embeddingResult = await model(inputs)
30
+
31
+ const lastHiddenState = embeddingResult.last_hidden_state
32
+
33
+ const tokenCount = lastHiddenState.dims[1]
34
+ const embeddingSize = lastHiddenState.dims[2]
35
+
36
+ for (let i = 0; i < tokenCount; i++) {
37
+ const tokenEmbedding = lastHiddenState.data.slice(i * embeddingSize, (i + 1) * embeddingSize)
38
+
39
+ const tokenId = inputTokens[i]
40
+
41
+ logger.log(`Token ${i} (${tokenId}):`, tokenEmbedding);
42
+ }
43
+
44
+ logger.log(inputTokens)
45
+ }
46
+
47
+ const languageNameToNLLBCode: Record<string, string> = {
48
+ 'Acehnese (Arabic script)': 'ace_Arab',
49
+ 'Acehnese (Latin script)': 'ace_Latn',
50
+ 'Afrikaans': 'afr_Latn',
51
+ 'Akan': 'aka_Latn',
52
+ 'Amharic': 'amh_Ethi',
53
+ 'Armenian': 'hye_Armn',
54
+ 'Assamese': 'asm_Beng',
55
+ 'Asturian': 'ast_Latn',
56
+ 'Awadhi': 'awa_Deva',
57
+ 'Ayacucho Quechua': 'quy_Latn',
58
+ 'Balinese': 'ban_Latn',
59
+ 'Bambara': 'bam_Latn',
60
+ 'Banjar (Arabic script)': 'bjn_Arab',
61
+ 'Banjar (Latin script)': 'bjn_Latn',
62
+ 'Bashkir': 'bak_Cyrl',
63
+ 'Basque': 'eus_Latn',
64
+ 'Belarusian': 'bel_Cyrl',
65
+ 'Bemba': 'bem_Latn',
66
+ 'Bengali': 'ben_Beng',
67
+ 'Bhojpuri': 'bho_Deva',
68
+ 'Bosnian': 'bos_Latn',
69
+ 'Buginese': 'bug_Latn',
70
+ 'Bulgarian': 'bul_Cyrl',
71
+ 'Burmese': 'mya_Mymr',
72
+ 'Catalan': 'cat_Latn',
73
+ 'Cebuano': 'ceb_Latn',
74
+ 'Central Atlas Tamazight': 'tzm_Tfng',
75
+ 'Central Aymara': 'ayr_Latn',
76
+ 'Central Kanuri (Arabic script)': 'knc_Arab',
77
+ 'Central Kanuri (Latin script)': 'knc_Latn',
78
+ 'Central Kurdish': 'ckb_Arab',
79
+ 'Chhattisgarhi': 'hne_Deva',
80
+ 'Chinese (Simplified)': 'zho_Hans',
81
+ 'Chinese (Traditional)': 'zho_Hant',
82
+ 'Chokwe': 'cjk_Latn',
83
+ 'Crimean Tatar': 'crh_Latn',
84
+ 'Croatian': 'hrv_Latn',
85
+ 'Czech': 'ces_Latn',
86
+ 'Danish': 'dan_Latn',
87
+ 'Dari': 'prs_Arab',
88
+ 'Dutch': 'nld_Latn',
89
+ 'Dyula': 'dyu_Latn',
90
+ 'Dzongkha': 'dzo_Tibt',
91
+ 'Eastern Panjabi': 'pan_Guru',
92
+ 'Eastern Yiddish': 'ydd_Hebr',
93
+ 'Egyptian Arabic': 'arz_Arab',
94
+ 'English': 'eng_Latn',
95
+ 'Esperanto': 'epo_Latn',
96
+ 'Estonian': 'est_Latn',
97
+ 'Ewe': 'ewe_Latn',
98
+ 'Faroese': 'fao_Latn',
99
+ 'Fijian': 'fij_Latn',
100
+ 'Finnish': 'fin_Latn',
101
+ 'Fon': 'fon_Latn',
102
+ 'French': 'fra_Latn',
103
+ 'Friulian': 'fur_Latn',
104
+ 'Galician': 'glg_Latn',
105
+ 'Ganda': 'lug_Latn',
106
+ 'Georgian': 'kat_Geor',
107
+ 'German': 'deu_Latn',
108
+ 'Greek': 'ell_Grek',
109
+ 'Guarani': 'grn_Latn',
110
+ 'Gujarati': 'guj_Gujr',
111
+ 'Haitian Creole': 'hat_Latn',
112
+ 'Halh Mongolian': 'khk_Cyrl',
113
+ 'Hausa': 'hau_Latn',
114
+ 'Hebrew': 'heb_Hebr',
115
+ 'Hindi': 'hin_Deva',
116
+ 'Hungarian': 'hun_Latn',
117
+ 'Icelandic': 'isl_Latn',
118
+ 'Igbo': 'ibo_Latn',
119
+ 'Ilocano': 'ilo_Latn',
120
+ 'Indonesian': 'ind_Latn',
121
+ 'Irish': 'gle_Latn',
122
+ 'Italian': 'ita_Latn',
123
+ 'Japanese': 'jpn_Jpan',
124
+ 'Javanese': 'jav_Latn',
125
+ 'Jingpho': 'kac_Latn',
126
+ 'Kabiyè': 'kbp_Latn',
127
+ 'Kabuverdianu': 'kea_Latn',
128
+ 'Kabyle': 'kab_Latn',
129
+ 'Kamba': 'kam_Latn',
130
+ 'Kannada': 'kan_Knda',
131
+ 'Kashmiri (Arabic script)': 'kas_Arab',
132
+ 'Kashmiri (Devanagari script)': 'kas_Deva',
133
+ 'Kazakh': 'kaz_Cyrl',
134
+ 'Khmer': 'khm_Khmr',
135
+ 'Kikongo': 'kon_Latn',
136
+ 'Kikuyu': 'kik_Latn',
137
+ 'Kimbundu': 'kmb_Latn',
138
+ 'Kinyarwanda': 'kin_Latn',
139
+ 'Korean': 'kor_Hang',
140
+ 'Kyrgyz': 'kir_Cyrl',
141
+ 'Lao': 'lao_Laoo',
142
+ 'Latgalian': 'ltg_Latn',
143
+ 'Ligurian': 'lij_Latn',
144
+ 'Limburgish': 'lim_Latn',
145
+ 'Lingala': 'lin_Latn',
146
+ 'Lithuanian': 'lit_Latn',
147
+ 'Lombard': 'lmo_Latn',
148
+ 'Luba-Kasai': 'lua_Latn',
149
+ 'Luo': 'luo_Latn',
150
+ 'Luxembourgish': 'ltz_Latn',
151
+ 'Macedonian': 'mkd_Cyrl',
152
+ 'Magahi': 'mag_Deva',
153
+ 'Maithili': 'mai_Deva',
154
+ 'Malayalam': 'mal_Mlym',
155
+ 'Maltese': 'mlt_Latn',
156
+ 'Maori': 'mri_Latn',
157
+ 'Marathi': 'mar_Deva',
158
+ 'Meitei (Bengali script)': 'mni_Beng',
159
+ 'Mesopotamian Arabic': 'acm_Arab',
160
+ 'Minangkabau (Arabic script)': 'min_Arab',
161
+ 'Minangkabau (Latin script)': 'min_Latn',
162
+ 'Mizo': 'lus_Latn',
163
+ 'Modern Standard Arabic (Romanized)': 'arb_Latn',
164
+ 'Modern Standard Arabic': 'arb_Arab',
165
+ 'Moroccan Arabic': 'ary_Arab',
166
+ 'Mossi': 'mos_Latn',
167
+ 'Najdi Arabic': 'ars_Arab',
168
+ 'Nepali': 'npi_Deva',
169
+ 'Nigerian Fulfulde': 'fuv_Latn',
170
+ 'North Azerbaijani': 'azj_Latn',
171
+ 'North Levantine Arabic': 'apc_Arab',
172
+ 'Northern Kurdish': 'kmr_Latn',
173
+ 'Northern Sotho': 'nso_Latn',
174
+ 'Northern Uzbek': 'uzn_Latn',
175
+ 'Norwegian Bokmål': 'nob_Latn',
176
+ 'Norwegian Nynorsk': 'nno_Latn',
177
+ 'Nuer': 'nus_Latn',
178
+ 'Nyanja': 'nya_Latn',
179
+ 'Occitan': 'oci_Latn',
180
+ 'Odia': 'ory_Orya',
181
+ 'Pangasinan': 'pag_Latn',
182
+ 'Papiamento': 'pap_Latn',
183
+ 'Plateau Malagasy': 'plt_Latn',
184
+ 'Polish': 'pol_Latn',
185
+ 'Portuguese': 'por_Latn',
186
+ 'Romanian': 'ron_Latn',
187
+ 'Rundi': 'run_Latn',
188
+ 'Russian': 'rus_Cyrl',
189
+ 'Samoan': 'smo_Latn',
190
+ 'Sango': 'sag_Latn',
191
+ 'Sanskrit': 'san_Deva',
192
+ 'Santali': 'sat_Olck',
193
+ 'Sardinian': 'srd_Latn',
194
+ 'Scottish Gaelic': 'gla_Latn',
195
+ 'Serbian': 'srp_Cyrl',
196
+ 'Shan': 'shn_Mymr',
197
+ 'Shona': 'sna_Latn',
198
+ 'Sicilian': 'scn_Latn',
199
+ 'Silesian': 'szl_Latn',
200
+ 'Sindhi': 'snd_Arab',
201
+ 'Sinhala': 'sin_Sinh',
202
+ 'Slovak': 'slk_Latn',
203
+ 'Slovenian': 'slv_Latn',
204
+ 'Somali': 'som_Latn',
205
+ 'South Azerbaijani': 'azb_Arab',
206
+ 'South Levantine Arabic': 'ajp_Arab',
207
+ 'Southern Pashto': 'pbt_Arab',
208
+ 'Southern Sotho': 'sot_Latn',
209
+ 'Southwestern Dinka': 'dik_Latn',
210
+ 'Spanish': 'spa_Latn',
211
+ 'Standard Latvian': 'lvs_Latn',
212
+ 'Standard Malay': 'zsm_Latn',
213
+ 'Standard Tibetan': 'bod_Tibt',
214
+ 'Sundanese': 'sun_Latn',
215
+ 'Swahili': 'swh_Latn',
216
+ 'Swati': 'ssw_Latn',
217
+ 'Swedish': 'swe_Latn',
218
+ 'Tagalog': 'tgl_Latn',
219
+ 'Tajik': 'tgk_Cyrl',
220
+ 'Tamasheq (Latin script)': 'taq_Latn',
221
+ 'Tamasheq (Tifinagh script)': 'taq_Tfng',
222
+ 'Tamil': 'tam_Taml',
223
+ 'Tatar': 'tat_Cyrl',
224
+ 'Ta’izzi-Adeni Arabic': 'acq_Arab',
225
+ 'Telugu': 'tel_Telu',
226
+ 'Thai': 'tha_Thai',
227
+ 'Tigrinya': 'tir_Ethi',
228
+ 'Tok Pisin': 'tpi_Latn',
229
+ 'Tosk Albanian': 'als_Latn',
230
+ 'Tsonga': 'tso_Latn',
231
+ 'Tswana': 'tsn_Latn',
232
+ 'Tumbuka': 'tum_Latn',
233
+ 'Tunisian Arabic': 'aeb_Arab',
234
+ 'Turkish': 'tur_Latn',
235
+ 'Turkmen': 'tuk_Latn',
236
+ 'Twi': 'twi_Latn',
237
+ 'Ukrainian': 'ukr_Cyrl',
238
+ 'Umbundu': 'umb_Latn',
239
+ 'Urdu': 'urd_Arab',
240
+ 'Uyghur': 'uig_Arab',
241
+ 'Venetian': 'vec_Latn',
242
+ 'Vietnamese': 'vie_Latn',
243
+ 'Waray': 'war_Latn',
244
+ 'Welsh': 'cym_Latn',
245
+ 'West Central Oromo': 'gaz_Latn',
246
+ 'Western Persian': 'pes_Arab',
247
+ 'Wolof': 'wol_Latn',
248
+ 'Xhosa': 'xho_Latn',
249
+ 'Yoruba': 'yor_Latn',
250
+ 'Yue Chinese': 'yue_Hant',
251
+ 'Zulu': 'zul_Latn',
252
+ }
@@ -182,4 +182,7 @@ const packageVersionTagResolutionLookup: { [packageName: string]: string } = {
182
182
 
183
183
  'whisper.cpp-binaries-windows-x64-cublas-12.4.0-latest-patched': '20240409',
184
184
  'whisper.cpp-binaries-windows-x64-cublas-11.8.0-latest-patched': '20240409',
185
+
186
+ 'xenova-multilingual-e5-small-quantized': '20240504',
187
+ 'xenova-nllb-200-distilled-600M-quantized': '20240505',
185
188
  }