echogarden 1.4.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +1 -1
  2. package/data/schemas/options.json +267 -19
  3. package/data/tables/lcid-table.json +9 -0
  4. package/dist/alignment/DTWMfccSequenceAlignment.d.ts +1 -1
  5. package/dist/alignment/DTWMfccSequenceAlignment.js +9 -5
  6. package/dist/alignment/DTWMfccSequenceAlignment.js.map +1 -1
  7. package/dist/alignment/DTWSequenceAlignmentWindowed.js +4 -4
  8. package/dist/alignment/DTWSequenceAlignmentWindowed.js.map +1 -1
  9. package/dist/alignment/{TextAlignment.d.ts → SemanticTextAlignment.d.ts} +10 -1
  10. package/dist/alignment/SemanticTextAlignment.js +336 -0
  11. package/dist/alignment/SemanticTextAlignment.js.map +1 -0
  12. package/dist/alignment/SpeechAlignment.d.ts +2 -1
  13. package/dist/alignment/SpeechAlignment.js +106 -38
  14. package/dist/alignment/SpeechAlignment.js.map +1 -1
  15. package/dist/api/API.d.ts +7 -2
  16. package/dist/api/API.js +7 -2
  17. package/dist/api/API.js.map +1 -1
  18. package/dist/api/APIOptions.d.ts +4 -1
  19. package/dist/api/Alignment.d.ts +1 -1
  20. package/dist/api/Alignment.js +14 -6
  21. package/dist/api/Alignment.js.map +1 -1
  22. package/dist/api/LanguageDetectionCommon.d.ts +6 -0
  23. package/dist/api/LanguageDetectionCommon.js +2 -0
  24. package/dist/api/LanguageDetectionCommon.js.map +1 -0
  25. package/dist/api/Recognition.js.map +1 -1
  26. package/dist/api/{LanguageDetection.d.ts → SpeechLanguageDetection.d.ts} +1 -25
  27. package/dist/api/{LanguageDetection.js → SpeechLanguageDetection.js} +1 -68
  28. package/dist/api/SpeechLanguageDetection.js.map +1 -0
  29. package/dist/api/{Translation.js → SpeechTranslation.js} +3 -3
  30. package/dist/api/SpeechTranslation.js.map +1 -0
  31. package/dist/api/Synthesis.js +4 -4
  32. package/dist/api/TextLanguageDetection.d.ts +21 -0
  33. package/dist/api/TextLanguageDetection.js +67 -0
  34. package/dist/api/TextLanguageDetection.js.map +1 -0
  35. package/dist/api/TextTranslation.d.ts +5 -0
  36. package/dist/api/TextTranslation.js +4 -0
  37. package/dist/api/TextTranslation.js.map +1 -0
  38. package/dist/api/TimelineTranslationAlignment.d.ts +23 -0
  39. package/dist/api/TimelineTranslationAlignment.js +92 -0
  40. package/dist/api/TimelineTranslationAlignment.js.map +1 -0
  41. package/dist/api/TranscriptAndTranslationAlignment.d.ts +35 -0
  42. package/dist/api/TranscriptAndTranslationAlignment.js +78 -0
  43. package/dist/api/TranscriptAndTranslationAlignment.js.map +1 -0
  44. package/dist/api/TranslationAlignment.d.ts +4 -3
  45. package/dist/api/TranslationAlignment.js +9 -8
  46. package/dist/api/TranslationAlignment.js.map +1 -1
  47. package/dist/api/VoiceActivityDetection.js +16 -1
  48. package/dist/api/VoiceActivityDetection.js.map +1 -1
  49. package/dist/cli/CLI.d.ts +27 -7
  50. package/dist/cli/CLI.js +205 -34
  51. package/dist/cli/CLI.js.map +1 -1
  52. package/dist/codecs/FFMpegTranscoder.js +7 -0
  53. package/dist/codecs/FFMpegTranscoder.js.map +1 -1
  54. package/dist/dsp/FFT.d.ts +1 -1
  55. package/dist/dsp/FFT.js +6 -0
  56. package/dist/dsp/FFT.js.map +1 -1
  57. package/dist/dsp/KWeightingFilter.js +1 -1
  58. package/dist/dsp/KWeightingFilter.js.map +1 -1
  59. package/dist/dsp/MelSpectogram.d.ts +3 -2
  60. package/dist/dsp/MelSpectogram.js +14 -8
  61. package/dist/dsp/MelSpectogram.js.map +1 -1
  62. package/dist/math/VectorMath.d.ts +22 -20
  63. package/dist/math/VectorMath.js +57 -30
  64. package/dist/math/VectorMath.js.map +1 -1
  65. package/dist/recognition/WhisperCppSTT.d.ts +1 -1
  66. package/dist/recognition/WhisperCppSTT.js +2 -2
  67. package/dist/recognition/WhisperCppSTT.js.map +1 -1
  68. package/dist/recognition/WhisperSTT.js +9 -6
  69. package/dist/recognition/WhisperSTT.js.map +1 -1
  70. package/dist/server/Client.d.ts +3 -2
  71. package/dist/server/Client.js.map +1 -1
  72. package/dist/server/Worker.d.ts +3 -2
  73. package/dist/server/Worker.js +3 -2
  74. package/dist/server/Worker.js.map +1 -1
  75. package/dist/source-separation/MDXNetSourceSeparation.js +1 -1
  76. package/dist/source-separation/MDXNetSourceSeparation.js.map +1 -1
  77. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.d.ts +13 -0
  78. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js +68 -0
  79. package/dist/speech-embeddings/WavToVec2BertFeatureEmbeddings.js.map +1 -0
  80. package/dist/speech-language-detection/SileroLanguageDetection.d.ts +1 -1
  81. package/dist/speech-language-detection/SileroLanguageDetection.js +1 -1
  82. package/dist/speech-language-detection/SileroLanguageDetection.js.map +1 -1
  83. package/dist/synthesis/EspeakTTS.js +4 -5
  84. package/dist/synthesis/EspeakTTS.js.map +1 -1
  85. package/dist/tests/Test.js +0 -8
  86. package/dist/tests/Test.js.map +1 -1
  87. package/dist/text-language-detection/FastTextLanguageDetection.d.ts +1 -1
  88. package/dist/text-language-detection/FastTextLanguageDetection.js.map +1 -1
  89. package/dist/text-language-detection/TinyLDLanguageDetection.d.ts +1 -1
  90. package/dist/text-language-detection/TinyLDLanguageDetection.js.map +1 -1
  91. package/dist/text-translation/NLLBTextTranslation.js +1 -1
  92. package/dist/text-translation/NLLBTextTranslation.js.map +1 -1
  93. package/dist/utilities/Locale.d.ts +1 -1
  94. package/dist/utilities/Locale.js +1 -1
  95. package/dist/utilities/OnnxUtilities.d.ts +1 -1
  96. package/dist/utilities/PackageManager.js +8 -2
  97. package/dist/utilities/PackageManager.js.map +1 -1
  98. package/dist/utilities/Timeline.d.ts +1 -0
  99. package/dist/utilities/Timeline.js +12 -0
  100. package/dist/utilities/Timeline.js.map +1 -1
  101. package/docs/API.md +81 -3
  102. package/docs/CLI.md +51 -1
  103. package/docs/Engines.md +24 -0
  104. package/docs/Options.md +33 -1
  105. package/docs/Tasklist.md +4 -1
  106. package/package.json +11 -11
  107. package/src/alignment/DTWMfccSequenceAlignment.ts +11 -5
  108. package/src/alignment/DTWSequenceAlignmentWindowed.ts +4 -4
  109. package/src/alignment/SemanticTextAlignment.ts +467 -0
  110. package/src/alignment/SpeechAlignment.ts +180 -53
  111. package/src/api/API.ts +18 -2
  112. package/src/api/APIOptions.ts +14 -1
  113. package/src/api/Alignment.ts +32 -10
  114. package/src/api/LanguageDetectionCommon.ts +7 -0
  115. package/src/api/Recognition.ts +2 -0
  116. package/src/api/{LanguageDetection.ts → SpeechLanguageDetection.ts} +1 -119
  117. package/src/api/{Translation.ts → SpeechTranslation.ts} +2 -2
  118. package/src/api/Synthesis.ts +4 -4
  119. package/src/api/TextLanguageDetection.ts +116 -0
  120. package/src/api/TextTranslation.ts +9 -0
  121. package/src/api/TimelineTranslationAlignment.ts +162 -0
  122. package/src/api/TranscriptAndTranslationAlignment.ts +164 -0
  123. package/src/api/TranslationAlignment.ts +12 -10
  124. package/src/api/VoiceActivityDetection.ts +24 -3
  125. package/src/cli/CLI.ts +276 -34
  126. package/src/codecs/FFMpegTranscoder.ts +6 -0
  127. package/src/dsp/FFT.ts +8 -2
  128. package/src/dsp/KWeightingFilter.ts +1 -1
  129. package/src/dsp/MelSpectogram.ts +17 -8
  130. package/src/math/VectorMath.ts +87 -52
  131. package/src/recognition/WhisperCppSTT.ts +2 -2
  132. package/src/recognition/WhisperSTT.ts +10 -6
  133. package/src/server/Client.ts +3 -2
  134. package/src/server/Worker.ts +3 -2
  135. package/src/source-separation/MDXNetSourceSeparation.ts +1 -1
  136. package/src/speech-embeddings/WavToVec2BertFeatureEmbeddings.ts +107 -0
  137. package/src/speech-language-detection/SileroLanguageDetection.ts +2 -1
  138. package/src/synthesis/EspeakTTS.ts +6 -8
  139. package/src/tests/Test.ts +1 -12
  140. package/src/text-language-detection/FastTextLanguageDetection.ts +1 -1
  141. package/src/text-language-detection/TinyLDLanguageDetection.ts +1 -1
  142. package/src/text-translation/NLLBTextTranslation.ts +1 -1
  143. package/src/utilities/Locale.ts +1 -1
  144. package/src/utilities/OnnxUtilities.ts +1 -1
  145. package/src/utilities/PackageManager.ts +9 -2
  146. package/src/utilities/Timeline.ts +14 -0
  147. package/dist/alignment/TextAlignment.js +0 -63
  148. package/dist/alignment/TextAlignment.js.map +0 -1
  149. package/dist/api/LanguageDetection.js.map +0 -1
  150. package/dist/api/Translation.js.map +0 -1
  151. package/src/alignment/TextAlignment.ts +0 -96
  152. /package/dist/api/{Translation.d.ts → SpeechTranslation.d.ts} +0 -0
@@ -1,3 +1,5 @@
1
+ import { clip } from "../utilities/Utilities.js"
2
+
1
3
  export function covarianceMatrixOfSamples(samples: number[][], weights?: number[], biased = false) {
2
4
  if (samples.length == 0) {
3
5
  throw new Error('No vectors given')
@@ -175,7 +177,7 @@ export function scaleToSumTo1(vector: number[]) {
175
177
  return scaledVector
176
178
  }
177
179
 
178
- export function normalizeVector(vector: number[], kind: 'population' | 'sample' = 'population') {
180
+ export function normalizeVector(vector: ArrayLike<number>, kind: 'population' | 'sample' = 'population') {
179
181
  if (vector.length == 0) {
180
182
  throw new Error('Vector is empty')
181
183
  }
@@ -327,15 +329,15 @@ export function varianceOfVectors(vectors: number[][], kind: 'population' | 'sam
327
329
  return result
328
330
  }
329
331
 
330
- export function meanOfVector(vector: number[]) {
332
+ export function meanOfVector(vector: ArrayLike<number>) {
331
333
  if (vector.length == 0) {
332
- throw new Error('Vector is empty')
334
+ return 0
333
335
  }
334
336
 
335
337
  return sumVector(vector) / vector.length
336
338
  }
337
339
 
338
- export function medianOfVector(vector: number[]) {
340
+ export function medianOfVector(vector: ArrayLike<number>) {
339
341
  if (vector.length == 0) {
340
342
  throw new Error('Vector is empty')
341
343
  }
@@ -343,13 +345,13 @@ export function medianOfVector(vector: number[]) {
343
345
  return vector[Math.floor(vector.length / 2)]
344
346
  }
345
347
 
346
- export function stdDeviationOfVector(vector: number[], kind: 'population' | 'sample' = 'population', mean?: number) {
348
+ export function stdDeviationOfVector(vector: ArrayLike<number>, kind: 'population' | 'sample' = 'population', mean?: number) {
347
349
  return Math.sqrt(varianceOfVector(vector, kind, mean))
348
350
  }
349
351
 
350
- export function varianceOfVector(vector: number[], kind: 'population' | 'sample' = 'population', mean?: number) {
352
+ export function varianceOfVector(vector: ArrayLike<number>, kind: 'population' | 'sample' = 'population', mean?: number) {
351
353
  if (vector.length == 0) {
352
- throw new Error('Vector is empty')
354
+ return 0
353
355
  }
354
356
 
355
357
  const sampleSizeMetric = kind == 'population' || vector.length == 1 ? vector.length : vector.length - 1
@@ -360,8 +362,8 @@ export function varianceOfVector(vector: number[], kind: 'population' | 'sample'
360
362
 
361
363
  let result = 0.0
362
364
 
363
- for (const value of vector) {
364
- result += (value - mean) ** 2
365
+ for (let i = 0; i < vector.length; i++) {
366
+ result += (vector[i] - mean) ** 2
365
367
  }
366
368
 
367
369
  return result / sampleSizeMetric
@@ -434,7 +436,7 @@ export function averageMeanSquaredError(actual: number[][], expected: number[][]
434
436
  return sum / vectorCount
435
437
  }
436
438
 
437
- export function meanSquaredError(actual: number[], expected: number[]) {
439
+ export function meanSquaredError(actual: ArrayLike<number>, expected: ArrayLike<number>) {
438
440
  if (actual.length != expected.length) {
439
441
  throw new Error('Vectors are not the same length')
440
442
  }
@@ -454,18 +456,18 @@ export function meanSquaredError(actual: number[], expected: number[]) {
454
456
  return sum / featureCount
455
457
  }
456
458
 
457
- export function euclidianDistance(vector1: number[], vector2: number[]) {
459
+ export function euclidianDistance(vector1: ArrayLike<number>, vector2: ArrayLike<number>) {
458
460
  return Math.sqrt(squaredEuclidianDistance(vector1, vector2))
459
461
  }
460
462
 
461
- export function squaredEuclidianDistance(vector1: number[], vector2: number[]) {
462
- if (vector1.length != vector2.length) {
463
+ export function squaredEuclidianDistance(vector1: ArrayLike<number>, vector2: ArrayLike<number>) {
464
+ if (vector1.length !== vector2.length) {
463
465
  throw new Error('Vectors are not the same length')
464
466
  }
465
467
 
466
468
  const elementCount = vector1.length
467
469
 
468
- if (elementCount == 0) {
470
+ if (elementCount === 0) {
469
471
  return 0
470
472
  }
471
473
 
@@ -478,16 +480,41 @@ export function squaredEuclidianDistance(vector1: number[], vector2: number[]) {
478
480
  return sum
479
481
  }
480
482
 
481
- export function cosineDistance(vector1: number[], vector2: number[]) {
483
+ export function euclidianDistance13Dim(vector1: ArrayLike<number>, vector2: ArrayLike<number>) {
484
+ return Math.sqrt(squaredEuclidianDistance13Dim(vector1, vector2))
485
+ }
486
+
487
+ export function squaredEuclidianDistance13Dim(vector1: ArrayLike<number>, vector2: ArrayLike<number>) {
488
+ // Assumes the input has 13 dimensions (optimized for 13-dimensional MFCC vectors)
489
+
490
+ const result =
491
+ (vector1[0] - vector2[0]) ** 2 +
492
+ (vector1[1] - vector2[1]) ** 2 +
493
+ (vector1[2] - vector2[2]) ** 2 +
494
+ (vector1[3] - vector2[3]) ** 2 +
495
+ (vector1[4] - vector2[4]) ** 2 +
496
+ (vector1[5] - vector2[5]) ** 2 +
497
+ (vector1[6] - vector2[6]) ** 2 +
498
+ (vector1[7] - vector2[7]) ** 2 +
499
+ (vector1[8] - vector2[8]) ** 2 +
500
+ (vector1[9] - vector2[9]) ** 2 +
501
+ (vector1[10] - vector2[10]) ** 2 +
502
+ (vector1[11] - vector2[11]) ** 2 +
503
+ (vector1[12] - vector2[12]) ** 2
504
+
505
+ return result
506
+ }
507
+
508
+ export function cosineDistance(vector1: ArrayLike<number>, vector2: ArrayLike<number>) {
482
509
  return 1 - cosineSimilarity(vector1, vector2)
483
510
  }
484
511
 
485
- export function cosineSimilarity(vector1: number[], vector2: number[]) {
486
- if (vector1.length != vector2.length) {
512
+ export function cosineSimilarity(vector1: ArrayLike<number>, vector2: ArrayLike<number>) {
513
+ if (vector1.length !== vector2.length) {
487
514
  throw new Error('Vectors are not the same length')
488
515
  }
489
516
 
490
- if (vector1.length == 0) {
517
+ if (vector1.length === 0) {
491
518
  return 0
492
519
  }
493
520
 
@@ -499,42 +526,28 @@ export function cosineSimilarity(vector1: number[], vector2: number[]) {
499
526
  let squaredMagnitude2 = 0.0
500
527
 
501
528
  for (let i = 0; i < elementCount; i++) {
502
- dotProduct += vector1[i] * vector2[i]
503
-
504
- squaredMagnitude1 += vector1[i] ** 2
505
- squaredMagnitude2 += vector2[i] ** 2
506
- }
507
-
508
- const result = dotProduct / (Math.sqrt(squaredMagnitude1) * Math.sqrt(squaredMagnitude2) + 1e-40)
509
-
510
- return zeroIfNaN(result)
511
- }
529
+ const element1 = vector1[i]
530
+ const element2 = vector2[i]
512
531
 
513
- export function minkowskiDistance(vector1: number[], vector2: number[], power: number) {
514
- if (vector1.length != vector2.length) {
515
- throw new Error('Vectors are not the same length')
516
- }
517
-
518
- const elementCount = vector1.length
532
+ dotProduct += element1 * element2
519
533
 
520
- if (elementCount == 0) {
521
- return 0
534
+ squaredMagnitude1 += element1 ** 2
535
+ squaredMagnitude2 += element2 ** 2
522
536
  }
523
537
 
524
- let sum = 0.0
538
+ let result = dotProduct / (Math.sqrt(squaredMagnitude1) * Math.sqrt(squaredMagnitude2) + 1e-40)
525
539
 
526
- for (let i = 0; i < elementCount; i++) {
527
- sum += Math.abs(vector1[i] - vector2[i]) ** power
528
- }
540
+ result = zeroIfNaN(result)
541
+ result = clip(result, -1.0, 1.0)
529
542
 
530
- return sum ** (1 / power)
543
+ return result
531
544
  }
532
545
 
533
- export function cosineDistancePrecomputedMagnitudes(vector1: number[], vector2: number[], magnitude1: number, magnitude2: number) {
546
+ export function cosineDistancePrecomputedMagnitudes(vector1: ArrayLike<number>, vector2: ArrayLike<number>, magnitude1: number, magnitude2: number) {
534
547
  return 1 - cosineSimilarityPrecomputedMagnitudes(vector1, vector2, magnitude1, magnitude2)
535
548
  }
536
549
 
537
- export function cosineSimilarityPrecomputedMagnitudes(vector1: number[], vector2: number[], magnitude1: number, magnitude2: number) {
550
+ export function cosineSimilarityPrecomputedMagnitudes(vector1: ArrayLike<number>, vector2: ArrayLike<number>, magnitude1: number, magnitude2: number) {
538
551
  if (vector1.length != vector2.length) {
539
552
  throw new Error('Vectors are not the same length')
540
553
  }
@@ -551,11 +564,33 @@ export function cosineSimilarityPrecomputedMagnitudes(vector1: number[], vector2
551
564
  dotProduct += vector1[i] * vector2[i]
552
565
  }
553
566
 
554
- const result = dotProduct / (magnitude1 * magnitude2)
567
+ let result = dotProduct / (magnitude1 * magnitude2 + 1e-40)
555
568
 
556
- return zeroIfNaN(result)
569
+ result = zeroIfNaN(result)
570
+ result = clip(result, -1.0, 1.0)
571
+
572
+ return result
557
573
  }
558
574
 
575
+ export function minkowskiDistance(vector1: ArrayLike<number>, vector2: ArrayLike<number>, power: number) {
576
+ if (vector1.length != vector2.length) {
577
+ throw new Error('Vectors are not the same length')
578
+ }
579
+
580
+ const elementCount = vector1.length
581
+
582
+ if (elementCount == 0) {
583
+ return 0
584
+ }
585
+
586
+ let sum = 0.0
587
+
588
+ for (let i = 0; i < elementCount; i++) {
589
+ sum += Math.abs(vector1[i] - vector2[i]) ** power
590
+ }
591
+
592
+ return sum ** (1 / power)
593
+ }
559
594
 
560
595
  export function subtractVectors(vector1: number[], vector2: number[]) {
561
596
  if (vector1.length != vector2.length) {
@@ -571,7 +606,7 @@ export function subtractVectors(vector1: number[], vector2: number[]) {
571
606
  return result
572
607
  }
573
608
 
574
- export function sumVector(vector: number[]) {
609
+ export function sumVector(vector: ArrayLike<number>) {
575
610
  let result = 0.0
576
611
 
577
612
  for (let i = 0; i < vector.length; i++) {
@@ -581,7 +616,7 @@ export function sumVector(vector: number[]) {
581
616
  return result
582
617
  }
583
618
 
584
- export function dotProduct(vector1: number[], vector2: number[]) {
619
+ export function dotProduct(vector1: ArrayLike<number>, vector2: ArrayLike<number>) {
585
620
  if (vector1.length != vector2.length) {
586
621
  throw new Error('Vectors are not the same length')
587
622
  }
@@ -597,7 +632,7 @@ export function dotProduct(vector1: number[], vector2: number[]) {
597
632
  return result
598
633
  }
599
634
 
600
- export function magnitude(vector: number[]) {
635
+ export function magnitude(vector: ArrayLike<number>) {
601
636
  const featureCount = vector.length
602
637
 
603
638
  let squaredMagnitude = 0.0
@@ -609,11 +644,11 @@ export function magnitude(vector: number[]) {
609
644
  return Math.sqrt(squaredMagnitude)
610
645
  }
611
646
 
612
- export function maxValue(vector: number[]) {
647
+ export function maxValue(vector: ArrayLike<number>) {
613
648
  return vector[indexOfMax(vector)]
614
649
  }
615
650
 
616
- export function indexOfMax(vector: number[]) {
651
+ export function indexOfMax(vector: ArrayLike<number>) {
617
652
  if (vector.length == 0) {
618
653
  return -1
619
654
  }
@@ -631,11 +666,11 @@ export function indexOfMax(vector: number[]) {
631
666
  return result
632
667
  }
633
668
 
634
- export function minValue(vector: number[]) {
669
+ export function minValue(vector: ArrayLike<number>) {
635
670
  return vector[indexOfMin(vector)]
636
671
  }
637
672
 
638
- export function indexOfMin(vector: number[]) {
673
+ export function indexOfMin(vector: ArrayLike<number>) {
639
674
  let minValue = Infinity
640
675
  let result = -1
641
676
 
@@ -13,7 +13,7 @@ import { splitToLines } from '../nlp/Segmentation.js'
13
13
  import { extendDeep } from '../utilities/ObjectUtilities.js'
14
14
  import { formatLanguageCodeWithName, getShortLanguageCode } from '../utilities/Locale.js'
15
15
  import { loadPackage } from '../utilities/PackageManager.js'
16
- import { detectSpeechLanguageByParts } from '../api/LanguageDetection.js'
16
+ import { detectSpeechLanguageByParts } from '../api/SpeechLanguageDetection.js'
17
17
 
18
18
  export async function recognize(
19
19
  sourceRawAudio: RawAudio,
@@ -201,7 +201,7 @@ export async function recognize(
201
201
 
202
202
  export async function detectLanguage(sourceRawAudio: RawAudio, modelName: WhisperModelName, modelPath: string) {
203
203
  if (sourceRawAudio.sampleRate != 16000) {
204
- throw new Error('Source audio must have a sampling rate of 16000')
204
+ throw new Error('Source audio must have a sample rate of 16000')
205
205
  }
206
206
 
207
207
  async function detectLanguageForPart(partAudio: RawAudio) {
@@ -17,7 +17,7 @@ import { formatLanguageCodeWithName, getShortLanguageCode, languageCodeToName }
17
17
  import { loadPackage } from '../utilities/PackageManager.js'
18
18
  import chalk from 'chalk'
19
19
  import { XorShift32RNG } from '../utilities/RandomGenerator.js'
20
- import { detectSpeechLanguageByParts } from '../api/LanguageDetection.js'
20
+ import { detectSpeechLanguageByParts } from '../api/SpeechLanguageDetection.js'
21
21
  import { type Tiktoken } from 'tiktoken/lite'
22
22
  import { isPunctuation, isWhitespace, isWord, splitToSentences, splitToWords } from '../nlp/Segmentation.js'
23
23
  import { medianOf5Filter } from '../math/MedianFilter.js'
@@ -35,7 +35,7 @@ export async function recognize(
35
35
  options = extendDeep(defaultWhisperOptions, options)
36
36
 
37
37
  if (sourceRawAudio.sampleRate != 16000) {
38
- throw new Error('Source audio must have a sampling rate of 16000 Hz')
38
+ throw new Error('Source audio must have a sample rate of 16000 Hz')
39
39
  }
40
40
 
41
41
  sourceLanguage = getShortLanguageCode(sourceLanguage)
@@ -87,7 +87,7 @@ export async function align(
87
87
  options = extendDeep(defaultWhisperAlignmentOptions, options)
88
88
 
89
89
  if (sourceRawAudio.sampleRate != 16000) {
90
- throw new Error('Source audio must have a sampling rate of 16000')
90
+ throw new Error('Source audio must have a sample rate of 16000 Hz')
91
91
  }
92
92
 
93
93
  sourceLanguage = getShortLanguageCode(sourceLanguage)
@@ -128,7 +128,7 @@ export async function alignEnglishTranslation(
128
128
  options = extendDeep(defaultWhisperAlignmentOptions, options)
129
129
 
130
130
  if (sourceRawAudio.sampleRate != 16000) {
131
- throw new Error('Source audio must have a sampling rate of 16000')
131
+ throw new Error('Source audio must have a sample rate of 16000 Hz')
132
132
  }
133
133
 
134
134
  sourceLanguage = getShortLanguageCode(sourceLanguage)
@@ -167,7 +167,7 @@ export async function detectLanguage(
167
167
  options = extendDeep(defaultWhisperLanguageDetectionOptions, options)
168
168
 
169
169
  if (sourceRawAudio.sampleRate != 16000) {
170
- throw new Error('Source audio must have a sampling rate of 16000')
170
+ throw new Error('Source audio must have a sample rate of 16000 Hz')
171
171
  }
172
172
 
173
173
  if (!isMultilingualModel(modelName)) {
@@ -213,7 +213,7 @@ export async function detectVoiceActivity(
213
213
  options = extendDeep(defaultWhisperVADOptions, options)
214
214
 
215
215
  if (sourceRawAudio.sampleRate != 16000) {
216
- throw new Error('Source audio must have a sampling rate of 16000')
216
+ throw new Error('Source audio must have a sample rate of 16000 Hz')
217
217
  }
218
218
 
219
219
  if (options.temperature! < 0) {
@@ -1089,6 +1089,10 @@ export class Whisper {
1089
1089
  const maxAudioSamples = sampleRate * 30
1090
1090
  const maxAudioFrames = 3000
1091
1091
 
1092
+ if (sampleRate != 16000) {
1093
+ throw new Error('Audio must have a sample rate of 16000 Hz')
1094
+ }
1095
+
1092
1096
  if (audioSamples.length > maxAudioSamples) {
1093
1097
  throw new Error(`Audio part is longer than 30 seconds`)
1094
1098
  }
@@ -7,9 +7,10 @@ import { OpenPromise } from '../utilities/OpenPromise.js'
7
7
  import { AudioSourceParam, RawAudio } from '../audio/AudioUtilities.js'
8
8
  import { AlignmentOptions, AlignmentResult } from '../api/Alignment.js'
9
9
  import { RecognitionOptions, RecognitionResult } from '../api/Recognition.js'
10
- import { SpeechTranslationOptions, SpeechTranslationResult } from '../api/Translation.js'
10
+ import { SpeechTranslationOptions, SpeechTranslationResult } from '../api/SpeechTranslation.js'
11
11
  import { Worker as WorkerThread } from 'node:worker_threads'
12
- import { SpeechLanguageDetectionOptions, SpeechLanguageDetectionResult, TextLanguageDetectionOptions, TextLanguageDetectionResult } from '../api/LanguageDetection.js'
12
+ import { SpeechLanguageDetectionOptions, SpeechLanguageDetectionResult } from '../api/SpeechLanguageDetection.js'
13
+ import { TextLanguageDetectionOptions, TextLanguageDetectionResult } from '../api/TextLanguageDetection.js'
13
14
 
14
15
  const log = logToStderr
15
16
 
@@ -4,13 +4,14 @@ import { logToStderr, yieldToEventLoop } from '../utilities/Utilities.js'
4
4
  import { AudioSourceParam } from '../audio/AudioUtilities.js'
5
5
  import { RecognitionOptions, RecognitionResult, recognize } from '../api/Recognition.js'
6
6
  import { AlignmentOptions, AlignmentResult, align } from '../api/Alignment.js'
7
- import { SpeechTranslationOptions, SpeechTranslationResult, translateSpeech } from '../api/Translation.js'
7
+ import { SpeechTranslationOptions, SpeechTranslationResult, translateSpeech } from '../api/SpeechTranslation.js'
8
8
  import { resetActiveLogger } from '../utilities/Logger.js'
9
9
  import { writeToStderr } from '../utilities/Utilities.js'
10
10
  import { Worker, SHARE_ENV } from 'node:worker_threads'
11
- import { SpeechLanguageDetectionOptions, SpeechLanguageDetectionResult, TextLanguageDetectionOptions, TextLanguageDetectionResult, detectSpeechLanguage, detectTextLanguage } from '../api/LanguageDetection.js'
11
+ import { SpeechLanguageDetectionOptions, SpeechLanguageDetectionResult, detectSpeechLanguage } from '../api/SpeechLanguageDetection.js'
12
12
  import chalk from 'chalk'
13
13
  import { resolveToModuleRootDir } from '../utilities/PathUtilities.js'
14
+ import { TextLanguageDetectionOptions, TextLanguageDetectionResult, detectTextLanguage } from '../api/TextLanguageDetection.js'
14
15
 
15
16
  const log = logToStderr
16
17
 
@@ -32,7 +32,7 @@ export class MDXNet {
32
32
  }
33
33
 
34
34
  if (rawAudio.sampleRate != 44100) {
35
- throw new Error(`Input audio must have a 44100 Hz sampling rate`)
35
+ throw new Error(`Input audio must have a sample rate of 44100 Hz`)
36
36
  }
37
37
 
38
38
  await this.initializeSessionIfNeeded()
@@ -0,0 +1,107 @@
1
+ import type * as Onnx from 'onnxruntime-node'
2
+ import { OnnxExecutionProvider, getOnnxSessionOptions } from '../utilities/OnnxUtilities.js';
3
+
4
+ import { RawAudio } from "../audio/AudioUtilities.js";
5
+ import { computeMelSpectogram } from "../dsp/MelSpectogram.js";
6
+ import { Logger } from '../utilities/Logger.js';
7
+ import { concatFloat32Arrays, splitFloat32Array } from '../utilities/Utilities.js';
8
+ import { applyEmphasis } from '../dsp/MFCC.js';
9
+
10
+ export function computeEmbeddings(audioSamples: RawAudio, modelFilePath: string, executionProviders: OnnxExecutionProvider[]) {
11
+ const wav2vecBert = new Wav2Vec2BertFeatureEmbeddings(
12
+ modelFilePath,
13
+ executionProviders,
14
+ )
15
+
16
+ const result = wav2vecBert.computeEmbeddings(audioSamples)
17
+
18
+ return result
19
+ }
20
+
21
+ export class Wav2Vec2BertFeatureEmbeddings {
22
+ session?: Onnx.InferenceSession
23
+
24
+ constructor(
25
+ public readonly modelFilePath: string,
26
+ public readonly executionProviders: OnnxExecutionProvider[]) {
27
+ }
28
+
29
+ async computeEmbeddings(rawAudio: RawAudio) {
30
+ const logger = new Logger()
31
+
32
+ rawAudio.audioChannels[0] = applyEmphasis(rawAudio.audioChannels[0], 0.97)
33
+
34
+ const { melSpectogram } = await computeMelSpectogram(
35
+ rawAudio,
36
+ 512,
37
+ 400,
38
+ 160,
39
+ 80,
40
+ 20,
41
+ 8000,
42
+ 'povey')
43
+
44
+ // Ensure even length
45
+ if (melSpectogram.length % 2 != 0) {
46
+ melSpectogram.push(new Float32Array(80))
47
+ }
48
+
49
+ // Normalize filterbanks
50
+ for (let filterbankIndex = 0; filterbankIndex < 80; filterbankIndex++) {
51
+ let sum = 0
52
+ let sumOfSquares = 0
53
+
54
+ for (let i = 0; i < melSpectogram.length; i++) {
55
+ const value = melSpectogram[i][filterbankIndex]
56
+
57
+ sum += value
58
+ sumOfSquares += value ** 2
59
+ }
60
+
61
+ const mean = sum / melSpectogram.length
62
+ const normalizationFactor = 1 / (Math.sqrt(sumOfSquares / melSpectogram.length) + 1e-40)
63
+
64
+ for (let i = 0; i < melSpectogram.length; i++) {
65
+ melSpectogram[i][filterbankIndex] -= mean
66
+ melSpectogram[i][filterbankIndex] *= normalizationFactor
67
+ }
68
+ }
69
+
70
+ // Flatten
71
+ const flattenedMelSpectogram = concatFloat32Arrays(melSpectogram)
72
+
73
+ // Initialize session
74
+ await this.initializeSessionIfNeeded()
75
+
76
+ const session = this.session!
77
+
78
+ const Onnx = await import('onnxruntime-node')
79
+
80
+ const inputTensor = new Onnx.Tensor('float32', flattenedMelSpectogram, [1, melSpectogram.length / 2, 80 * 2])
81
+
82
+ const attentionMask = new Int32Array(melSpectogram.length / 2).fill(1)
83
+ const attentionMaskTensor = new Onnx.Tensor('int32', attentionMask, [1, attentionMask.length])
84
+
85
+ // Run inference
86
+ const outputs = await session.run({ 'input_features': inputTensor, 'attention_mask': attentionMaskTensor })
87
+
88
+ // Return output
89
+ const lastHiddenStateData = outputs['last_hidden_state'].data as Float32Array
90
+
91
+ const outputEmbeddings = splitFloat32Array(lastHiddenStateData, outputs['last_hidden_state'].dims[2])
92
+
93
+ return outputEmbeddings
94
+ }
95
+
96
+ private async initializeSessionIfNeeded() {
97
+ if (this.session) {
98
+ return
99
+ }
100
+
101
+ const Onnx = await import('onnxruntime-node')
102
+
103
+ const onnxSessionOptions = getOnnxSessionOptions({ executionProviders: this.executionProviders })
104
+
105
+ this.session = await Onnx.InferenceSession.create(this.modelFilePath, onnxSessionOptions)
106
+ }
107
+ }
@@ -3,9 +3,10 @@ import { softmax } from '../math/VectorMath.js'
3
3
  import { Logger } from '../utilities/Logger.js'
4
4
  import { RawAudio } from '../audio/AudioUtilities.js'
5
5
  import { readAndParseJsonFile } from '../utilities/FileSystem.js'
6
- import { detectSpeechLanguageByParts, type LanguageDetectionResults } from '../api/LanguageDetection.js'
6
+ import { detectSpeechLanguageByParts } from '../api/SpeechLanguageDetection.js'
7
7
  import { languageCodeToName } from '../utilities/Locale.js'
8
8
  import { OnnxExecutionProvider, getOnnxSessionOptions } from '../utilities/OnnxUtilities.js'
9
+ import { LanguageDetectionResults } from '../api/LanguageDetectionCommon.js'
9
10
 
10
11
  export async function detectLanguage(
11
12
  rawAudio: RawAudio,
@@ -140,13 +140,11 @@ export async function preprocessAndSynthesize(text: string, language: string, es
140
140
  }
141
141
 
142
142
  export async function synthesizeFragments(fragments: string[], espeakOptions: EspeakOptions) {
143
- const logger = new Logger()
144
-
145
143
  espeakOptions = extendDeep(defaultEspeakOptions, espeakOptions)
146
144
 
147
- const sampleRate = await getSampleRate()
145
+ const voice = espeakOptions.voice
148
146
 
149
- //fragments = fragments.filter(fragment => fragment.trim() != '')
147
+ const sampleRate = await getSampleRate()
150
148
 
151
149
  if (fragments.length === 0) {
152
150
  return {
@@ -156,9 +154,11 @@ export async function synthesizeFragments(fragments: string[], espeakOptions: Es
156
154
  }
157
155
  }
158
156
 
157
+ const canInsertSeparators = !['roa/an', 'art/eo', 'trk/ky', 'zlw/pl', 'zle/uk'].includes(voice)
158
+
159
159
  let textWithMarkers: string
160
160
 
161
- if (espeakOptions.insertSeparators || !espeakOptions.voice.endsWith('/pl')) {
161
+ if (canInsertSeparators) {
162
162
  textWithMarkers = `() | `
163
163
  } else {
164
164
  textWithMarkers = `() `
@@ -173,7 +173,7 @@ export async function synthesizeFragments(fragments: string[], espeakOptions: Es
173
173
  .replaceAll('<', '&lt;')
174
174
  .replaceAll('>', '&gt;')
175
175
 
176
- if (espeakOptions.insertSeparators) {
176
+ if (espeakOptions.insertSeparators && canInsertSeparators) {
177
177
  const separator = ` | `
178
178
 
179
179
  textWithMarkers += `<mark name="s-${i}"/>${separator}${fragment}${separator}<mark name="e-${i}"/>`
@@ -186,8 +186,6 @@ export async function synthesizeFragments(fragments: string[], espeakOptions: Es
186
186
  }
187
187
  }
188
188
 
189
- //log(textWithMarkers)
190
-
191
189
  const { rawAudio, events } = await synthesize(textWithMarkers, { ...espeakOptions, ssml: true })
192
190
 
193
191
  // Add first marker if missing
package/src/tests/Test.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { getTokenRepetitionScore, logToStderr, setupProgramTerminationListeners, writeToStderr } from '../utilities/Utilities.js'
2
1
  import { makeTarballsForInstalledPackages } from '../utilities/TarballMaker.js'
3
- import { testEspeakSynthesisWithPrePhonemizedInputs, testKirshenbaumPhonemization } from '../synthesis/EspeakTTS.js'
2
+ import { logToStderr, setupProgramTerminationListeners } from '../utilities/Utilities.js'
4
3
 
5
4
  const log = logToStderr
6
5
 
@@ -9,16 +8,6 @@ setupProgramTerminationListeners()
9
8
  //process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
10
9
  //process.env.http_proxy = 'http://localhost:8080'
11
10
 
12
- //const testText = `After a while, finding that nothing more happened, she decided on going into the garden at once; but, alas for poor Alice! when she got to the door, she found she had forgotten the little golden key, and when she went back to the table for it, she found she could not possibly reach it: she could see it quite plainly through the glass, and she tried her best to climb up one of the legs of the table, but it was too slippery; and when she had tired herself out with trying, the poor little thing sat down and cried.`
13
- //await testKirshenbaumPhonemization(testText)
14
-
15
- //getRepetitionScoreRelativeToFirstSubstring(['a', 'b', 'c', 'a', 'b', 'c'])
16
- //getRepetitionScoreRelativeToFirstSubstring(['a', 'b', 'a', 'd', 'a', 'b', 'a', 'd'])
17
- //getRepetitionScoreRelativeToFirstSubstring(['a', 'b', 'a', 'b', 'c', 'a', 'b', 'a', 'b'])
18
- //getRepetitionScoreRelativeToFirstSubstring(['a', 'a', 'a', 'b', 'a', 'a', 'a', 'b'])
19
- //getRepetitionScoreRelativeToFirstSubstring(['a', 'b', 'a', 'c', 'a', 'b', 'a', 'c', 'a'])
20
- //getRepetitionScoreRelativeToFirstSubstring(['a', 'a', 'a', 'b', 'b', 'a', 'a', 'a', 'b'])
21
-
22
11
  //await makeTarballsForInstalledPackages(true)
23
12
 
24
13
  process.exit(0)
@@ -1,8 +1,8 @@
1
1
  import path from 'path'
2
- import type { LanguageDetectionResults } from '../api/LanguageDetection.js'
3
2
  import { languageCodeToName } from '../utilities/Locale.js'
4
3
  import { OpenPromise } from '../utilities/OpenPromise.js'
5
4
  import { resolveModuleMainPath, roundToDigits } from '../utilities/Utilities.js'
5
+ import { LanguageDetectionResults } from '../api/LanguageDetectionCommon.js'
6
6
 
7
7
  let fastTextLanguageDetectionModel: any
8
8
 
@@ -1,6 +1,6 @@
1
- import type { LanguageDetectionResults } from '../api/LanguageDetection.js'
2
1
  import { detectAll } from 'tinyld'
3
2
  import { languageCodeToName } from '../utilities/Locale.js'
3
+ import { LanguageDetectionResults } from '../api/LanguageDetectionCommon.js'
4
4
 
5
5
  export async function detectLanguage(text: string) {
6
6
  const tinyldResults = detectAll(text)
@@ -6,7 +6,7 @@ export async function translateText(sourceText: string, sourceLanguage: string,
6
6
 
7
7
  const { AutoTokenizer, M2M100ForConditionalGeneration } = await import('@echogarden/transformers-nodejs-lite')
8
8
 
9
- const modelPath = await loadPackage(`xenova-nllb-200-distilled-600M-quantized`)
9
+ const modelPath = await loadPackage(`xenova-nllb-200-distilled-600M-q8`)
10
10
 
11
11
  const tokenizer = await AutoTokenizer.from_pretrained(modelPath)
12
12
  const model = await M2M100ForConditionalGeneration.from_pretrained(modelPath)
@@ -22,7 +22,7 @@ export function formatLanguageCodeWithName(languageCode: string, styleId: 1 | 2
22
22
  }
23
23
  }
24
24
 
25
- export async function normalizeIdentifierToLangaugeCode(langIdentifier: string) {
25
+ export async function normalizeIdentifierToLanguageCode(langIdentifier: string) {
26
26
  const result = await parseLangIdentifier(langIdentifier)
27
27
 
28
28
  return result.Name
@@ -65,4 +65,4 @@ export interface OnnxSessionOptions {
65
65
  logSeverityLevel?: 0 | 1 | 2 | 3 | 4
66
66
  }
67
67
 
68
- export type OnnxExecutionProvider = 'cpu' | 'dml'
68
+ export type OnnxExecutionProvider = 'cpu' | 'dml' | 'cuda'
@@ -183,6 +183,13 @@ const packageVersionTagResolutionLookup: { [packageName: string]: string } = {
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
185
 
186
- 'xenova-multilingual-e5-small-quantized': '20240504',
187
- 'xenova-nllb-200-distilled-600M-quantized': '20240505',
186
+ 'xenova-multilingual-e5-small-q8': '20240504',
187
+ 'xenova-nllb-200-distilled-600M-q8': '20240505',
188
+ 'xenova-multilingual-e5-small-fp16': '20240514',
189
+ 'xenova-multilingual-e5-base-fp16': '20240514',
190
+ 'xenova-multilingual-e5-base-q8': '20240514',
191
+ 'xenova-multilingual-e5-large-q8': '20240514',
192
+
193
+ 'w2v-bert-2.0-int8': '20240517',
194
+ 'w2v-bert-2.0-uint8': '20240517',
188
195
  }