echogarden 1.8.2 → 1.8.4

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 (50) hide show
  1. package/dist/alignment/DTWMfccSequenceAlignment.d.ts +1 -1
  2. package/dist/alignment/DTWMfccSequenceAlignment.js +6 -5
  3. package/dist/alignment/DTWMfccSequenceAlignment.js.map +1 -1
  4. package/dist/alignment/DTWSequenceAlignment.d.ts +1 -1
  5. package/dist/alignment/DTWSequenceAlignment.js.map +1 -1
  6. package/dist/alignment/DTWSequenceAlignmentWindowed.d.ts +1 -1
  7. package/dist/alignment/DTWSequenceAlignmentWindowed.js.map +1 -1
  8. package/dist/alignment/SpeechAlignment.d.ts +2 -0
  9. package/dist/alignment/SpeechAlignment.js +1 -1
  10. package/dist/alignment/SpeechAlignment.js.map +1 -1
  11. package/dist/api/SpeechSearch.d.ts +6 -0
  12. package/dist/api/SpeechSearch.js +4 -0
  13. package/dist/api/SpeechSearch.js.map +1 -0
  14. package/dist/dsp/MFCC.d.ts +5 -6
  15. package/dist/dsp/MFCC.js +4 -19
  16. package/dist/dsp/MFCC.js.map +1 -1
  17. package/dist/math/MedianFilter.d.ts +2 -2
  18. package/dist/math/MedianFilter.js +3 -4
  19. package/dist/math/MedianFilter.js.map +1 -1
  20. package/dist/math/VectorMath.d.ts +34 -34
  21. package/dist/math/VectorMath.js +63 -44
  22. package/dist/math/VectorMath.js.map +1 -1
  23. package/dist/nlp/IPA.d.ts +3 -3
  24. package/dist/recognition/WhisperSTT.d.ts +1 -0
  25. package/dist/recognition/WhisperSTT.js +36 -20
  26. package/dist/recognition/WhisperSTT.js.map +1 -1
  27. package/dist/speech-search/DTWSpeechSearch.d.ts +2 -0
  28. package/dist/speech-search/DTWSpeechSearch.js +18 -0
  29. package/dist/speech-search/DTWSpeechSearch.js.map +1 -0
  30. package/dist/utilities/RandomGenerator.d.ts +2 -2
  31. package/dist/utilities/RandomGenerator.js.map +1 -1
  32. package/dist/utilities/Utilities.js +7 -1
  33. package/dist/utilities/Utilities.js.map +1 -1
  34. package/dist/utilities/WebReader.js +2 -2
  35. package/dist/utilities/WebReader.js.map +1 -1
  36. package/docs/Options.md +8 -6
  37. package/package.json +2 -2
  38. package/src/alignment/DTWMfccSequenceAlignment.ts +9 -6
  39. package/src/alignment/DTWSequenceAlignment.ts +2 -2
  40. package/src/alignment/DTWSequenceAlignmentWindowed.ts +3 -3
  41. package/src/alignment/SpeechAlignment.ts +1 -1
  42. package/src/api/SpeechSearch.ts +12 -0
  43. package/src/dsp/MFCC.ts +11 -31
  44. package/src/math/MedianFilter.ts +5 -5
  45. package/src/math/VectorMath.ts +109 -75
  46. package/src/recognition/WhisperSTT.ts +45 -27
  47. package/src/speech-search/DTWSpeechSearch.ts +26 -0
  48. package/src/utilities/RandomGenerator.ts +2 -2
  49. package/src/utilities/Utilities.ts +11 -3
  50. package/src/utilities/WebReader.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "echogarden",
3
- "version": "1.8.2",
3
+ "version": "1.8.4",
4
4
  "description": "An easy-to-use speech toolset. Includes tools for synthesis, recognition, alignment, speech translation, language detection, source separation and more.",
5
5
  "author": "Rotem Dan",
6
6
  "license": "GPL-3.0",
@@ -85,7 +85,7 @@
85
85
  "jsdom": "^25.0.1",
86
86
  "json5": "^2.2.3",
87
87
  "kuromoji": "^0.1.2",
88
- "microsoft-cognitiveservices-speech-sdk": "^1.40.0",
88
+ "microsoft-cognitiveservices-speech-sdk": "^1.41.0",
89
89
  "moving-median": "^1.0.0",
90
90
  "msgpack-lite": "^0.1.26",
91
91
  "onnxruntime-node": "^1.19.2",
@@ -1,10 +1,10 @@
1
- import { cosineDistancePrecomputedMagnitudes, createVectorForIntegerRange, euclideanDistance, euclideanDistance13Dim, magnitude } from '../math/VectorMath.js'
2
- import { logToStderr } from '../utilities/Utilities.js'
1
+ import { cosineDistancePrecomputedMagnitudes, euclideanDistance, euclideanDistance13Dim, magnitude } from '../math/VectorMath.js'
2
+ import { getIntegerRange, logToStderr } from '../utilities/Utilities.js'
3
3
  import { alignDTWWindowed } from './DTWSequenceAlignmentWindowed.js'
4
4
 
5
5
  const log = logToStderr
6
6
 
7
- export async function alignMFCC_DTW(mfccFrames1: number[][], mfccFrames2: number[][], windowLength: number, distanceFunctionKind: 'euclidean' | 'cosine' = 'euclidean', centerIndexes?: number[]) {
7
+ export async function alignMFCC_DTW(mfccFrames1: ArrayLike<number>[], mfccFrames2: ArrayLike<number>[], windowLength: number, distanceFunctionKind: 'euclidean' | 'cosine' = 'euclidean', centerIndexes?: number[]) {
8
8
  if (distanceFunctionKind == 'euclidean') {
9
9
  let distanceFunction = euclideanDistance
10
10
 
@@ -22,16 +22,19 @@ export async function alignMFCC_DTW(mfccFrames1: number[][], mfccFrames2: number
22
22
 
23
23
  return path
24
24
  } else if (distanceFunctionKind == 'cosine') {
25
- const indexes1 = createVectorForIntegerRange(0, mfccFrames1.length)
26
- const indexes2 = createVectorForIntegerRange(0, mfccFrames2.length)
25
+ const indexes1 = getIntegerRange(0, mfccFrames1.length)
26
+ const indexes2 = getIntegerRange(0, mfccFrames2.length)
27
27
 
28
28
  const magnitudes1 = mfccFrames1.map(magnitude)
29
29
  const magnitudes2 = mfccFrames2.map(magnitude)
30
30
 
31
+ const distanceFunction = (i: number, j: number) =>
32
+ cosineDistancePrecomputedMagnitudes(mfccFrames1[i], mfccFrames2[j], magnitudes1[i], magnitudes2[j])
33
+
31
34
  const { path } = alignDTWWindowed(
32
35
  indexes1,
33
36
  indexes2,
34
- (i, j) => cosineDistancePrecomputedMagnitudes(mfccFrames1[i], mfccFrames2[j], magnitudes1[i], magnitudes2[j]),
37
+ distanceFunction,
35
38
  windowLength,
36
39
  centerIndexes
37
40
  )
@@ -3,7 +3,7 @@ import { AlignmentPath } from './SpeechAlignment.js'
3
3
 
4
4
  const log = logToStderr
5
5
 
6
- export function alignDTW<T, U>(sequence1: T[], sequence2: U[], costFunction: (a: T, b: U) => number, deletionEnabled = true) {
6
+ export function alignDTW<T, U>(sequence1: ArrayLike<T>, sequence2: ArrayLike<U>, costFunction: (a: T, b: U) => number, deletionEnabled = true) {
7
7
  if (sequence1.length == 0 || sequence2.length == 0) {
8
8
  return { path: [] as AlignmentPath, pathCost: 0 }
9
9
  }
@@ -23,7 +23,7 @@ export function alignDTW<T, U>(sequence1: T[], sequence2: U[], costFunction: (a:
23
23
  return { path, pathCost }
24
24
  }
25
25
 
26
- function computeAccumulatedCostMatrix<T, U>(sequence1: T[], sequence2: U[], costFunction: (a: T, b: U) => number, deletionEnabled = true) {
26
+ function computeAccumulatedCostMatrix<T, U>(sequence1: ArrayLike<T>, sequence2: ArrayLike<U>, costFunction: (a: T, b: U) => number, deletionEnabled = true) {
27
27
  const rowCount = sequence2.length
28
28
  const columnCount = sequence1.length
29
29
 
@@ -3,7 +3,7 @@ import { AlignmentPath } from './SpeechAlignment.js'
3
3
 
4
4
  const log = logToStderr
5
5
 
6
- export function alignDTWWindowed<T, U>(sequence1: T[], sequence2: U[], costFunction: (a: T, b: U) => number, windowMaxLength: number, centerIndexes?: number[]) {
6
+ export function alignDTWWindowed<T, U>(sequence1: ArrayLike<T>, sequence2: ArrayLike<U>, costFunction: (a: T, b: U) => number, windowMaxLength: number, centerIndexes?: ArrayLike<number>) {
7
7
  windowMaxLength = Math.max(windowMaxLength, 2)
8
8
 
9
9
  if (sequence1.length == 0 || sequence2.length == 0) {
@@ -29,7 +29,7 @@ export function alignDTWWindowed<T, U>(sequence1: T[], sequence2: U[], costFunct
29
29
  return { path, pathCost }
30
30
  }
31
31
 
32
- function computeAccumulatedCostMatrixTransposed<T, U>(sequence1: T[], sequence2: U[], costFunction: (a: T, b: U) => number, windowMaxLength: number, centerIndexes?: number[]) {
32
+ function computeAccumulatedCostMatrixTransposed<T, U>(sequence1: ArrayLike<T>, sequence2: ArrayLike<U>, costFunction: (a: T, b: U) => number, windowMaxLength: number, centerIndexes?: ArrayLike<number>) {
33
33
  const halfWindowMaxLength = Math.floor(windowMaxLength / 2)
34
34
 
35
35
  const columnCount = sequence1.length
@@ -139,7 +139,7 @@ function computeAccumulatedCostMatrixTransposed<T, U>(sequence1: T[], sequence2:
139
139
  }
140
140
  }
141
141
 
142
- function computeBestPathTransposed(accumulatedCostMatrixTransposed: Float32Array[], windowStartOffsets: Int32Array) {
142
+ function computeBestPathTransposed(accumulatedCostMatrixTransposed: ArrayLike<number>[], windowStartOffsets: ArrayLike<number>) {
143
143
  const columnCount = accumulatedCostMatrixTransposed.length
144
144
  const rowCount = accumulatedCostMatrixTransposed[0].length
145
145
 
@@ -716,7 +716,7 @@ function getMappedFrameIndexForPath(referenceFrameIndex: number, compactedPath:
716
716
  return mappedFrameIndex
717
717
  }
718
718
 
719
- function getMfccOptionsForGranularity(granularity: DtwGranularity) {
719
+ export function getMfccOptionsForGranularity(granularity: DtwGranularity) {
720
720
  let mfccOptions: MfccOptions
721
721
 
722
722
  if (granularity == 'xx-low') {
@@ -0,0 +1,12 @@
1
+ import { AudioSourceParam } from "../audio/AudioUtilities.js";
2
+
3
+ export async function searchSpeech(inputAudio: AudioSourceParam, text: string, options: SpeechSearchOptions): Promise<SpeechSearchResult> {
4
+ return {}
5
+ }
6
+
7
+ export interface SpeechSearchOptions {
8
+
9
+ }
10
+
11
+ export interface SpeechSearchResult {
12
+ }
package/src/dsp/MFCC.ts CHANGED
@@ -36,8 +36,6 @@ export async function computeMFCCs(monoAudio: RawAudio, options: MfccOptions = {
36
36
  logger.start(`Resample audio to analysis sample rate (${analysisSampleRate}Hz)`)
37
37
  const resampledAudio = await resampleAudioSpeex(monoAudio, analysisSampleRate)
38
38
 
39
- let mfccs: number[][]
40
-
41
39
  if (emphasisFactor > 0) {
42
40
  logger.start('Apply emphasis')
43
41
  resampledAudio.audioChannels[0] = applyEmphasis(resampledAudio.audioChannels[0], emphasisFactor)
@@ -47,20 +45,19 @@ export async function computeMFCCs(monoAudio: RawAudio, options: MfccOptions = {
47
45
  const { melSpectogram } = await computeMelSpectogram(resampledAudio, fftOrder, windowSize, hopLength, filterbankCount, lowerFrequencyHz, upperFrequencyHz)
48
46
 
49
47
  logger.start('Extract MFCCs from Mel spectogram')
50
- const mfccsFloat32 = melSpectogramToMFCCs(melSpectogram, featureCount)
51
-
52
- mfccs = mfccsFloat32.map(mfcc => Array.from(mfcc))
48
+ let mfccs = melSpectogramToMFCCs(melSpectogram, featureCount)
53
49
 
54
50
  if (options.normalize!) {
55
51
  logger.start('Normalize MFCCs')
56
52
 
57
53
  const { normalizedVectors, mean, stdDeviation } = normalizeVectors(mfccs)
54
+
58
55
  mfccs = normalizedVectors
59
- //mfccs = mfccs.map(mfcc => subtractVectors(mfcc, mean))
60
56
  }
61
57
 
62
58
  if (lifteringFactor > 0) {
63
59
  logger.start('Apply liftering to MFCCs')
60
+
64
61
  mfccs = applyLiftering(mfccs, lifteringFactor)
65
62
  }
66
63
 
@@ -75,7 +72,7 @@ export async function computeMFCCs(monoAudio: RawAudio, options: MfccOptions = {
75
72
  return mfccs
76
73
  }
77
74
 
78
- export function melSpectogramToMFCCs(melSpectogram: Float32Array[], mfccFeatureCount: number) {
75
+ export function melSpectogramToMFCCs(melSpectogram: ArrayLike<number>[], mfccFeatureCount: number) {
79
76
  const melBandCount = melSpectogram[0].length
80
77
  const dctMatrix = createDCTType2CoefficientMatrix(mfccFeatureCount, melBandCount)
81
78
 
@@ -84,7 +81,7 @@ export function melSpectogramToMFCCs(melSpectogram: Float32Array[], mfccFeatureC
84
81
  return mfccs
85
82
  }
86
83
 
87
- export function melSpectrumToMFCC(melSpectrum: Float32Array, mfccFeatureCount: number, dctMatrix: Float32Array[], normalization: 'none' | 'orthonormal' = 'orthonormal') {
84
+ export function melSpectrumToMFCC(melSpectrum: ArrayLike<number>, mfccFeatureCount: number, dctMatrix: ArrayLike<number>[], normalization: 'none' | 'orthonormal' = 'orthonormal') {
88
85
  const melBandCount = melSpectrum.length
89
86
 
90
87
  let firstFeatureNormalizationFactor: number
@@ -108,7 +105,6 @@ export function melSpectrumToMFCC(melSpectrum: Float32Array, mfccFeatureCount: n
108
105
  for (let j = 0; j < melBandCount; j++) {
109
106
  const dctCoefficient = dctMatrixRow[j]
110
107
  const logMel = powerToDecibels(melSpectrum[j])
111
- //const logMel = Math.log(1e-40 + melSpectrum[j])
112
108
 
113
109
  sum += dctCoefficient * logMel
114
110
  }
@@ -123,7 +119,7 @@ export function melSpectrumToMFCC(melSpectrum: Float32Array, mfccFeatureCount: n
123
119
  }
124
120
 
125
121
  export function createDCTType2CoefficientMatrix(mfccFeatureCount: number, melBandCount: number) {
126
- const dctMatrix = new Array<Float32Array>(mfccFeatureCount)
122
+ const dctMatrix: Float32Array[] = []
127
123
 
128
124
  for (let mfccFeatureIndex = 0; mfccFeatureIndex < mfccFeatureCount; mfccFeatureIndex++) {
129
125
  const row = new Float32Array(melBandCount)
@@ -134,29 +130,13 @@ export function createDCTType2CoefficientMatrix(mfccFeatureCount: number, melBan
134
130
  row[melBandIndex] = Math.cos(innerMultiplier * (melBandIndex + 0.5))
135
131
  }
136
132
 
137
- dctMatrix[mfccFeatureIndex] = row
133
+ dctMatrix.push(row)
138
134
  }
139
135
 
140
136
  return dctMatrix
141
137
  }
142
138
 
143
- export function mfccBufferToVectors(mfccBuffer: Float64Array, mfccFeatureCount: number) {
144
- if (mfccBuffer.length % mfccFeatureCount != 0) {
145
- throw new Error(`MFCC buffer length is not a multiple of the expected feature count (${mfccFeatureCount})`)
146
- }
147
-
148
- const mfccVectors: number[][] = []
149
-
150
- for (let offset = 0; offset < mfccBuffer.length; offset += mfccFeatureCount) {
151
- const mfccVector = Array.from(mfccBuffer.subarray(offset, offset + mfccFeatureCount))
152
-
153
- mfccVectors.push(mfccVector)
154
- }
155
-
156
- return mfccVectors
157
- }
158
-
159
- export function applyEmphasis(samples: Float32Array, emphasisFactor = 0.97, initialState = 0) {
139
+ export function applyEmphasis(samples: ArrayLike<number>, emphasisFactor = 0.97, initialState = 0) {
160
140
  const processedSamples = new Float32Array(samples.length)
161
141
 
162
142
  processedSamples[0] = samples[0] - (emphasisFactor * initialState)
@@ -168,7 +148,7 @@ export function applyEmphasis(samples: Float32Array, emphasisFactor = 0.97, init
168
148
  return processedSamples
169
149
  }
170
150
 
171
- export function applyLiftering(mfccs: number[][], lifteringFactor: number) {
151
+ export function applyLiftering(mfccs: ArrayLike<number>[], lifteringFactor: number) {
172
152
  const featureCount = mfccs[0].length
173
153
 
174
154
  const lifterMultipliers = new Float32Array(featureCount)
@@ -177,10 +157,10 @@ export function applyLiftering(mfccs: number[][], lifteringFactor: number) {
177
157
  lifterMultipliers[i] = 1 + (lifteringFactor / 2) * Math.sin(Math.PI * (i + 1) / lifteringFactor)
178
158
  }
179
159
 
180
- const lifteredMfccs: number[][] = []
160
+ const lifteredMfccs: Float32Array[] = []
181
161
 
182
162
  for (const mfcc of mfccs) {
183
- const lifteredMfcc = new Array(featureCount)
163
+ const lifteredMfcc = new Float32Array(featureCount)
184
164
 
185
165
  for (let i = 0; i < featureCount; i++) {
186
166
  lifteredMfcc[i] = mfcc[i] * lifterMultipliers[i]
@@ -1,6 +1,6 @@
1
1
  import { createVector } from "./VectorMath.js"
2
2
 
3
- export function medianOf5Filter(points: number[]) {
3
+ export function medianOf5Filter(points: ArrayLike<number>): Float32Array {
4
4
  // This function computes the moving median with a window of 5 elements.
5
5
 
6
6
  // I initialized the window such that at the edges of the range no median would be computed.
@@ -10,10 +10,10 @@ export function medianOf5Filter(points: number[]) {
10
10
  const pointCount = points.length
11
11
 
12
12
  if (pointCount < 5) {
13
- return points
13
+ return Float32Array.from(points)
14
14
  }
15
15
 
16
- const medians = createVector(pointCount)
16
+ const medians = new Float32Array(pointCount)
17
17
 
18
18
  medians[0] = points[0]
19
19
  medians[1] = points[1]
@@ -38,7 +38,7 @@ export function medianOf3Filter(points: ArrayLike<number>) {
38
38
  return points
39
39
  }
40
40
 
41
- const medians: number[] = createVector(pointCount)
41
+ const medians = new Float32Array(pointCount)
42
42
 
43
43
  medians[0] = points[0]
44
44
  medians[pointCount - 1] = points[pointCount - 1]
@@ -110,7 +110,7 @@ export function medianOf3(a: number, b: number, c: number) {
110
110
  }
111
111
 
112
112
  // Slower, variable-width median filter using the `moving-median` package
113
- export async function medianFilter(points: number[], width: number) {
113
+ export async function medianFilter(points: ArrayLike<number>, width: number) {
114
114
  const { default: createMedianFilter } = await import('moving-median')
115
115
 
116
116
  const filter = createMedianFilter(width)