echogarden 1.3.2 → 1.4.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 (57) hide show
  1. package/data/schemas/options.json +3 -1
  2. package/dist/alignment/DTWSequenceAlignmentWindowed.js +68 -27
  3. package/dist/alignment/DTWSequenceAlignmentWindowed.js.map +1 -1
  4. package/dist/alignment/SpeechAlignment.d.ts +1 -1
  5. package/dist/alignment/SpeechAlignment.js +6 -21
  6. package/dist/alignment/SpeechAlignment.js.map +1 -1
  7. package/dist/api/Alignment.js +47 -28
  8. package/dist/api/Alignment.js.map +1 -1
  9. package/dist/api/LanguageDetection.js +3 -3
  10. package/dist/api/LanguageDetection.js.map +1 -1
  11. package/dist/api/Recognition.js +1 -1
  12. package/dist/api/Recognition.js.map +1 -1
  13. package/dist/api/Synthesis.js +1 -1
  14. package/dist/api/Synthesis.js.map +1 -1
  15. package/dist/api/Translation.js +1 -1
  16. package/dist/api/Translation.js.map +1 -1
  17. package/dist/api/TranslationAlignment.js +1 -1
  18. package/dist/api/TranslationAlignment.js.map +1 -1
  19. package/dist/audio/AudioBufferConversion.d.ts +2 -2
  20. package/dist/audio/AudioBufferConversion.js +46 -36
  21. package/dist/audio/AudioBufferConversion.js.map +1 -1
  22. package/dist/cli/CLI.js +1 -0
  23. package/dist/cli/CLI.js.map +1 -1
  24. package/dist/codecs/FFMpegTranscoder.js +2 -1
  25. package/dist/codecs/FFMpegTranscoder.js.map +1 -1
  26. package/dist/codecs/WaveCodec.js +8 -3
  27. package/dist/codecs/WaveCodec.js.map +1 -1
  28. package/dist/recognition/WhisperSTT.d.ts +1 -0
  29. package/dist/recognition/WhisperSTT.js +13 -6
  30. package/dist/recognition/WhisperSTT.js.map +1 -1
  31. package/dist/synthesis/EspeakTTS.js +5 -0
  32. package/dist/synthesis/EspeakTTS.js.map +1 -1
  33. package/dist/utilities/Compression.d.ts +1 -0
  34. package/dist/utilities/Compression.js +11 -1
  35. package/dist/utilities/Compression.js.map +1 -1
  36. package/dist/utilities/LEB128.d.ts +5 -0
  37. package/dist/utilities/LEB128.js +168 -0
  38. package/dist/utilities/LEB128.js.map +1 -0
  39. package/docs/Options.md +2 -1
  40. package/docs/Tasklist.md +3 -3
  41. package/package.json +7 -7
  42. package/src/alignment/DTWSequenceAlignmentWindowed.ts +69 -29
  43. package/src/alignment/SpeechAlignment.ts +7 -23
  44. package/src/api/Alignment.ts +48 -29
  45. package/src/api/LanguageDetection.ts +3 -3
  46. package/src/api/Recognition.ts +1 -1
  47. package/src/api/Synthesis.ts +1 -1
  48. package/src/api/Translation.ts +1 -1
  49. package/src/api/TranslationAlignment.ts +1 -1
  50. package/src/audio/AudioBufferConversion.ts +46 -36
  51. package/src/cli/CLI.ts +1 -0
  52. package/src/codecs/FFMpegTranscoder.ts +3 -1
  53. package/src/codecs/WaveCodec.ts +11 -3
  54. package/src/recognition/WhisperSTT.ts +14 -7
  55. package/src/synthesis/EspeakTTS.ts +7 -1
  56. package/src/utilities/Compression.ts +16 -1
  57. package/src/utilities/LEB128.ts +237 -0
@@ -0,0 +1,168 @@
1
+ import { logToStderr } from "./Utilities.js";
2
+ ////////////////////////////////////////////////////////////////////////////////////
3
+ // Encode
4
+ ////////////////////////////////////////////////////////////////////////////////////
5
+ export function encodeSignedInt32(value, outEncodedData) {
6
+ if (value < -2147483648 || value > 2147483647) {
7
+ throw new Error('Value must be between -2147483648 and 2147483647');
8
+ }
9
+ while (true) {
10
+ const lowest7Bits = value & 127;
11
+ value >>= 7;
12
+ if ((value === 0 && (lowest7Bits & 64) === 0) ||
13
+ (value === -1 && (lowest7Bits & 64) !== 0)) {
14
+ outEncodedData.push(lowest7Bits);
15
+ return outEncodedData;
16
+ }
17
+ else {
18
+ outEncodedData.push(lowest7Bits | 128);
19
+ }
20
+ }
21
+ }
22
+ export function encodeSignedInt32sFast(value, outEncodedData) {
23
+ const absValue = Math.abs(value);
24
+ //const absMask = value >> 31
25
+ //const absValue = (value ^ absMask) - absMask
26
+ if (absValue < (2 ** 6)) {
27
+ outEncodedData.push((value & 127));
28
+ }
29
+ else if (absValue < (2 ** 13)) {
30
+ outEncodedData.push((value & 127) | 128, (value >> 7) & 127);
31
+ }
32
+ else if (absValue < (2 ** 20)) {
33
+ outEncodedData.push((value & 127) | 128, ((value >> 7) & 127) | 128, (value >> 14) & 127);
34
+ }
35
+ else if (absValue < (2 ** 27)) {
36
+ outEncodedData.push((value & 127) | 128, ((value >> 7) & 127) | 128, ((value >> 14) & 127) | 128, (value >> 21) & 127);
37
+ }
38
+ else if (value < (2 ** 31) && value >= -(2 ** 31)) {
39
+ outEncodedData.push((value & 127) | 128, ((value >> 7) & 127) | 128, ((value >> 14) & 127) | 128, ((value >> 21) & 127) | 128, (value >> 28) & 127);
40
+ }
41
+ else {
42
+ throw new Error(`Value must be between -2147483648 and 2147483647`);
43
+ }
44
+ }
45
+ ////////////////////////////////////////////////////////////////////////////////////
46
+ // Decode
47
+ ////////////////////////////////////////////////////////////////////////////////////
48
+ export function decodeSignedInt32s(encodedData, outDecodedValues) {
49
+ for (let readIndex = 0; readIndex < encodedData.length;) {
50
+ let currentDecodedValue = 0;
51
+ let shiftAmount = 0;
52
+ while (true) {
53
+ const encodedByte = encodedData[readIndex++];
54
+ const lowest7Bits = encodedByte & 127;
55
+ currentDecodedValue |= lowest7Bits << shiftAmount;
56
+ // If 8th bit is 0, then this is the last byte in the sequence
57
+ if ((encodedByte & 128) === 0) {
58
+ // If 7th bit is 1, then the value is negative
59
+ if ((encodedByte & 64) !== 0) {
60
+ // If the value should be negative
61
+ // Ensure that the value is encoded as a negative number by
62
+ // setting all higher bits to 1
63
+ currentDecodedValue |= -1 << Math.min(shiftAmount + 7, 31);
64
+ }
65
+ break;
66
+ }
67
+ if (readIndex === encodedData.length) {
68
+ throw new Error(`Invalid LEB128 data. Last encoded byte sequence is truncated.`);
69
+ }
70
+ shiftAmount += 7;
71
+ if (shiftAmount > 31) {
72
+ throw new Error(`LEB128 sequence can't be decoded. Byte sequence extends beyond the range of a signed 32 bit integer.`);
73
+ }
74
+ }
75
+ outDecodedValues.push(currentDecodedValue);
76
+ }
77
+ return outDecodedValues;
78
+ }
79
+ export function decodeSignedInt32sFast(encodedData, outDecodedValues) {
80
+ for (let readIndex = 0; readIndex < encodedData.length;) {
81
+ const byte0 = encodedData[readIndex++];
82
+ if ((byte0 & 128) === 0) {
83
+ let decodedValue = (byte0 & 127);
84
+ if ((byte0 & 64) !== 0) {
85
+ decodedValue |= -1 << 7;
86
+ }
87
+ outDecodedValues.push(decodedValue);
88
+ continue;
89
+ }
90
+ const byte1 = encodedData[readIndex++];
91
+ if ((byte1 & 128) === 0) {
92
+ let decodedValue = (byte0 & 127) |
93
+ (byte1 & 127) << 7;
94
+ if ((byte1 & 64) !== 0) {
95
+ decodedValue |= -1 << 14;
96
+ }
97
+ outDecodedValues.push(decodedValue);
98
+ continue;
99
+ }
100
+ const byte2 = encodedData[readIndex++];
101
+ if ((byte2 & 128) === 0) {
102
+ let decodedValue = (byte0 & 127) |
103
+ (byte1 & 127) << 7 |
104
+ (byte2 & 127) << 14;
105
+ if ((byte2 & 64) !== 0) {
106
+ decodedValue |= -1 << 21;
107
+ }
108
+ outDecodedValues.push(decodedValue);
109
+ continue;
110
+ }
111
+ const byte3 = encodedData[readIndex++];
112
+ if ((byte3 & 128) === 0) {
113
+ let decodedValue = (byte0 & 127) |
114
+ (byte1 & 127) << 7 |
115
+ (byte2 & 127) << 14 |
116
+ (byte3 & 127) << 21;
117
+ if ((byte3 & 64) !== 0) {
118
+ decodedValue |= -1 << 28;
119
+ }
120
+ outDecodedValues.push(decodedValue);
121
+ continue;
122
+ }
123
+ const byte4 = encodedData[readIndex++];
124
+ if ((byte4 & 128) === 0) {
125
+ let decodedValue = (byte0 & 127) |
126
+ (byte1 & 127) << 7 |
127
+ (byte2 & 127) << 14 |
128
+ (byte3 & 127) << 21 |
129
+ (byte4 & 127) << 28;
130
+ if ((byte4 & 64) !== 0) {
131
+ decodedValue |= -1 << 31;
132
+ }
133
+ outDecodedValues.push(decodedValue);
134
+ continue;
135
+ }
136
+ if (readIndex >= encodedData.length) {
137
+ throw new Error(`Invalid LEB128 data. Last encoded byte sequence is truncated.`);
138
+ }
139
+ else {
140
+ throw new Error(`LEB128 sequence can't be decoded. Encoded byte sequence represents a value that extends beyond the range of a signed 32 bit integer.`);
141
+ }
142
+ }
143
+ return outDecodedValues;
144
+ }
145
+ ////////////////////////////////////////////////////////////////////////////////////
146
+ // Tests
147
+ ////////////////////////////////////////////////////////////////////////////////////
148
+ export function testLeb128() {
149
+ const encodedBytes = [];
150
+ const decodedValues = [];
151
+ function runTest(testValue) {
152
+ encodedBytes.length = 0;
153
+ decodedValues.length = 0;
154
+ encodeSignedInt32sFast(testValue, encodedBytes);
155
+ decodeSignedInt32sFast(encodedBytes, decodedValues);
156
+ if (decodedValues[0] !== testValue) {
157
+ throw new Error(`Expected ${testValue} but got ${decodedValues[0]}`);
158
+ }
159
+ }
160
+ for (let i = -(2 ** 22); i < 2 ** 22; i++) {
161
+ if (i % 1000000 === 0) {
162
+ logToStderr(i);
163
+ }
164
+ runTest(i);
165
+ }
166
+ const x = 1;
167
+ }
168
+ //# sourceMappingURL=LEB128.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LEB128.js","sourceRoot":"","sources":["../../src/utilities/LEB128.ts"],"names":[],"mappings":"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"}
package/docs/Options.md CHANGED
@@ -149,6 +149,7 @@ Applies to CLI operation: `transcribe`, API method: `recognize`
149
149
  * `whisper.autoPromptParts`: use previous part's recognized text as the prompt for the next part. Disabling this may help to prevent repetition carrying over between parts, in some cases. Defaults to `true`
150
150
  * `whisper.maxTokensPerPart`: maximum number of tokens to decode for each audio part. Defaults to `250`
151
151
  * `whisper.suppressRepetition`: attempt to suppress decoding of repeating token patterns. Defaults to `true`
152
+ * `whisper.repetitionThreshold`: minimal repetition / compressibility score to cause a part not to be auto-prompted to the next part. Defaults to `2.4`
152
153
  * `whisper.decodeTimestampTokens`: enable/disable decoding of timestamp tokens. Setting to `false` can reduce the occurrence of hallucinations and token repetition loops, possibly due to the overall reduction in the number of tokens decoded. This has no impact on the accuracy of timestamps, since they are derived independently using cross-attention weights. However, there are cases where this can cause the model to end a part prematurely, especially in singing and less speech-like voice segments, or when there are multiple speakers. Defaults to `true`
153
154
  * `whisper.encoderProvider`: identifier for the ONNX execution provider to use with the encoder model. Can be `cpu` or `dml` ([DirectML](https://microsoft.github.io/DirectML/)-based GPU acceleration - Windows only). In general, GPU-based encoding should be significantly faster. Defaults to `cpu`, or `dml` if available
154
155
  * `whisper.decoderProvider`: identifier for the ONNX execution provider to use with the decoder model. Can be `cpu` or `dml` (Windows only). Using GPU acceleration for the decoder may be faster than CPU, especially for larger models, but that depends on your particular combination of CPU and GPU. Defaults to `cpu`
@@ -221,7 +222,7 @@ Applies to CLI operation: `align`, API method: `align`
221
222
  * `plainText.whitespace`: determines how to process whitespace within transcript paragraphs. Can be `preserve` (leave as is), `removeLineBreaks` (convert line breaks to spaces) or `collapse` (convert runs of whitespace characters, including line breaks, to a single space character). Defaults to `collapse`
222
223
 
223
224
  **DTW**:
224
- * `dtw.granularity`: adjusts the MFCC frame width and hop size based on the profile selected. Can be set to either `auto` (auto-selected based on audio duration and task), `xx-low` (400ms width, 160ms hop), `x-low` (200ms width, 80ms hop), `low` (100ms width, 40ms hop), `medium` (50ms width, 20ms hop), `high` (25ms width, 10ms hop), `x-high` (20ms width, 5ms hop). For multi-pass processing, multiple granularities can be provided, like `dtw.granularity=['low','high']`. Defaults to `auto`.
225
+ * `dtw.granularity`: adjusts the MFCC frame width and hop size based on the profile selected. Can be set to either `xx-low` (400ms width, 160ms hop), `x-low` (200ms width, 80ms hop), `low` (100ms width, 40ms hop), `medium` (50ms width, 20ms hop), `high` (25ms width, 10ms hop), `x-high` (20ms width, 5ms hop). For multi-pass processing, multiple granularities can be provided, like `dtw.granularity=['xx-low','medium']`. Auto-selected by default.
225
226
  * `dtw.windowDuration`: maximum duration (in seconds) of the Sakoe-Chiba window when performing DTW alignment. Higher values consume quadratically larger amounts of memory. The estimated memory requirement is shown in the log before alignment starts. Recommended to be set to at least 10% - 20% of total audio duration. For multi-pass processing, multiple durations can be provided, like `dtw.windowDuration=[240,20]`. Auto-selected by default
226
227
 
227
228
  **DTW-RA**:
package/docs/Tasklist.md CHANGED
@@ -9,7 +9,8 @@
9
9
 
10
10
  ### Synthesis
11
11
 
12
- ### Phoneme processing
12
+ ### eSpeak
13
+
13
14
  * IPA -> Kirshenbaum translation is still not completely similar to what is output by eSpeak. Also, in rare situations, it outputs characters that are not accepted by eSpeak and eSpeak errors. Investigate when that happens and how to improve on this
14
15
 
15
16
  ### Browser extension
@@ -151,8 +152,7 @@
151
152
  * Show alternatives when playing in the CLI. Clear current line and rewrite already printed text for alternatives during the speech recognition process
152
153
 
153
154
  ### Recognition / Whisper
154
- * Whisper's Chinese and Japanese output can be split into words in a more accurate way. Consider using a dedicated segmentation library to perform the segmentation in character sequences that have no spaces within them
155
- * Automatically disable using previous section recognized transcript as prompt for the next section when lots of repetition occurred in previous section
155
+ * Whisper's Chinese and Japanese output can be split into words in a more accurate way. Consider using a dedicated segmentation library to perform the segmentation in character sequences that have no punctuation characters to aid on guessing word boundaries
156
156
  * Cache last model (if enough memory is available)
157
157
  * The segment output can be used to split into segments, otherwise it is possible to try to guess using pause lengths or voice activity detection
158
158
  * Bring back the option to use eSpeak DTW based alignment on segments, as an alternative approach
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "echogarden",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
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",
@@ -55,8 +55,8 @@
55
55
  "echogarden": "./dist/cli/CLILauncher.js"
56
56
  },
57
57
  "dependencies": {
58
- "@aws-sdk/client-polly": "^3.569.0",
59
- "@aws-sdk/client-transcribe-streaming": "^3.569.0",
58
+ "@aws-sdk/client-polly": "^3.574.0",
59
+ "@aws-sdk/client-transcribe-streaming": "^3.574.0",
60
60
  "@echogarden/espeak-ng-emscripten": "^0.1.2",
61
61
  "@echogarden/fasttext-wasm": "^0.1.0",
62
62
  "@echogarden/flite-wasi": "^0.1.1",
@@ -89,14 +89,14 @@
89
89
  "moving-median": "^1.0.0",
90
90
  "msgpack-lite": "^0.1.26",
91
91
  "onnxruntime-node": "^1.17.3",
92
- "openai": "^4.42.0",
92
+ "openai": "^4.45.0",
93
93
  "sam-js": "^0.2.1",
94
94
  "strip-ansi": "^7.1.0",
95
95
  "tar": "^7.1.0",
96
96
  "tiktoken": "^1.0.14",
97
97
  "tinyld": "^1.3.4",
98
98
  "ws": "^8.17.0",
99
- "wtf_wikipedia": "^10.3.0"
99
+ "wtf_wikipedia": "^10.3.1"
100
100
  },
101
101
  "peerDependencies": {
102
102
  "@echogarden/vosk": "^0.3.39-patched.1",
@@ -120,11 +120,11 @@
120
120
  "@types/graceful-fs": "^4.1.9",
121
121
  "@types/jsdom": "^21.1.6",
122
122
  "@types/msgpack-lite": "^0.1.11",
123
- "@types/node": "^20.12.10",
123
+ "@types/node": "^20.12.11",
124
124
  "@types/recursive-readdir": "^2.2.4",
125
125
  "@types/tar": "^6.1.13",
126
126
  "@types/ws": "^8.5.10",
127
- "ts-json-schema-generator": "^2.1.1",
127
+ "ts-json-schema-generator": "^2.1.2-next.1",
128
128
  "typescript": "^5.4.5"
129
129
  }
130
130
  }
@@ -1,5 +1,3 @@
1
- import chalk from 'chalk'
2
- import { Logger } from '../utilities/Logger.js'
3
1
  import { logToStderr } from '../utilities/Utilities.js'
4
2
  import { AlignmentPath } from './SpeechAlignment.js'
5
3
 
@@ -11,7 +9,10 @@ export function alignDTWWindowed<T, U>(sequence1: T[], sequence2: U[], costFunct
11
9
  }
12
10
 
13
11
  if (sequence1.length == 0 || sequence2.length == 0) {
14
- return { path: [] as AlignmentPath, pathCost: 0 }
12
+ return {
13
+ path: [] as AlignmentPath,
14
+ pathCost: 0
15
+ }
15
16
  }
16
17
 
17
18
  // Compute accumulated cost matrix (transposed)
@@ -38,10 +39,10 @@ function computeAccumulatedCostMatrixTransposed<T, U>(sequence1: T[], sequence2:
38
39
 
39
40
  const accumulatedCostMatrixTransposed: Float32Array[] = new Array<Float32Array>(columnCount)
40
41
 
41
- // Initialize window start offsets array
42
+ // Initialize an array to store window start offsets
42
43
  const windowStartOffsets = new Int32Array(columnCount)
43
44
 
44
- // Compute matrix column by column
45
+ // Compute accumulated cost matrix column by column
45
46
  for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
46
47
  // Create new column and add it to the matrix
47
48
  const currentColumn = new Float32Array(rowCount)
@@ -65,13 +66,13 @@ function computeAccumulatedCostMatrixTransposed<T, U>(sequence1: T[], sequence2:
65
66
  windowStartOffset = windowEndOffset - rowCount
66
67
  }
67
68
 
68
- // Store the start offset in the array
69
+ // Store the start offset for this column
69
70
  windowStartOffsets[columnIndex] = windowStartOffset
70
71
 
71
72
  // Get target sequence1 value
72
73
  const targetSequence1Value = sequence1[columnIndex]
73
74
 
74
- // If first column, fill it only using the 'up' neighbor
75
+ // If this is the first column, fill it only using the 'up' neighbors
75
76
  if (columnIndex == 0) {
76
77
  for (let rowIndex = 1; rowIndex < rowCount; rowIndex++) {
77
78
  const cost = costFunction(targetSequence1Value, sequence2[windowStartOffset + rowIndex])
@@ -84,11 +85,15 @@ function computeAccumulatedCostMatrixTransposed<T, U>(sequence1: T[], sequence2:
84
85
  }
85
86
 
86
87
  // If not first column
88
+
89
+ // Store the column to the left
87
90
  const leftColumn = accumulatedCostMatrixTransposed[columnIndex - 1]
88
91
 
89
- // Compute the delta between the current window offset and previous column's window offset
92
+ // Compute the delta between the current window start offset
93
+ // and left column's window offset
90
94
  const windowOffsetDelta = windowStartOffset - windowStartOffsets[columnIndex - 1]
91
95
 
96
+ // Iterate over all rows in the window
92
97
  for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
93
98
  // Compute the cost for current cell
94
99
  const cost = costFunction(targetSequence1Value, sequence2[windowStartOffset + rowIndex])
@@ -115,12 +120,25 @@ function computeAccumulatedCostMatrixTransposed<T, U>(sequence1: T[], sequence2:
115
120
  upAndLeftCost = leftColumn[upAndLeftRowIndex]
116
121
  }
117
122
 
123
+ // Find the minimum of all neighbors
124
+ let minimumNeighborCost = minimumOf3(upCost, leftCost, upAndLeftCost)
125
+
126
+ // If all neighbors are infinity, then it means there is a "jump" between the window
127
+ // of the current column and the left column, and they don't have overlapping rows.
128
+ // In this case, only the cost of the current cell will be used
129
+ if (minimumNeighborCost === Infinity) {
130
+ minimumNeighborCost = 0
131
+ }
132
+
118
133
  // Write cost + minimum neighbor cost to the current column
119
- currentColumn[rowIndex] = cost + minimumOf3(upCost, leftCost, upAndLeftCost)
134
+ currentColumn[rowIndex] = cost + minimumNeighborCost
120
135
  }
121
136
  }
122
137
 
123
- return { accumulatedCostMatrixTransposed, windowStartOffsets }
138
+ return {
139
+ accumulatedCostMatrixTransposed,
140
+ windowStartOffsets
141
+ }
124
142
  }
125
143
 
126
144
  function computeBestPathTransposed(accumulatedCostMatrixTransposed: Float32Array[], windowStartOffsets: Int32Array) {
@@ -129,6 +147,8 @@ function computeBestPathTransposed(accumulatedCostMatrixTransposed: Float32Array
129
147
 
130
148
  const bestPath: AlignmentPath = []
131
149
 
150
+ // Start at the bottom right corner and find the best path
151
+ // towards the top left
132
152
  let columnIndex = columnCount - 1
133
153
  let rowIndex = rowCount - 1
134
154
 
@@ -136,19 +156,21 @@ function computeBestPathTransposed(accumulatedCostMatrixTransposed: Float32Array
136
156
  const windowStartIndex = windowStartOffsets[columnIndex]
137
157
  const windowStartDelta = columnIndex > 0 ? windowStartIndex - windowStartOffsets[columnIndex - 1] : 0
138
158
 
159
+ // Add the current cell to the best path
139
160
  bestPath.push({
140
161
  source: columnIndex,
141
162
  dest: windowStartIndex + rowIndex
142
163
  })
143
164
 
165
+ // Retrieve the cost for the 'up' (insertion) neighbor
144
166
  const upRowIndex = rowIndex - 1
145
- const upColumnIndex = columnIndex
146
167
  let upCost = Infinity
147
168
 
148
169
  if (upRowIndex >= 0) {
149
- upCost = accumulatedCostMatrixTransposed[upColumnIndex][upRowIndex] // insertion
170
+ upCost = accumulatedCostMatrixTransposed[columnIndex][upRowIndex] // insertion
150
171
  }
151
172
 
173
+ // Retrieve the cost for the 'left' (deletion) neighbor
152
174
  const leftRowIndex = rowIndex + windowStartDelta
153
175
  const leftColumnIndex = columnIndex - 1
154
176
  let leftCost = Infinity
@@ -157,6 +179,7 @@ function computeBestPathTransposed(accumulatedCostMatrixTransposed: Float32Array
157
179
  leftCost = accumulatedCostMatrixTransposed[leftColumnIndex][leftRowIndex] // deletion
158
180
  }
159
181
 
182
+ // Retrieve the cost for the 'up and left' (match) neighbor
160
183
  const upAndLeftRowIndex = rowIndex - 1 + windowStartDelta
161
184
  const upAndLeftColumnIndex = columnIndex - 1
162
185
  let upAndLeftCost = Infinity
@@ -165,25 +188,42 @@ function computeBestPathTransposed(accumulatedCostMatrixTransposed: Float32Array
165
188
  upAndLeftCost = accumulatedCostMatrixTransposed[upAndLeftColumnIndex][upAndLeftRowIndex] // match
166
189
  }
167
190
 
191
+ // If all neighbors have a cost of infinity, it means
192
+ // there is a "jump" between the window for the current and previous column
168
193
  if (upCost == Infinity && leftCost == Infinity && upAndLeftCost == Infinity) {
169
- const logger = new Logger()
170
-
171
- logger.setAsActiveLogger()
172
- logger.logTitledMessage(`computeBestPath`, `unexpected - all cost directions are equal to infinity (${columnIndex}, ${rowIndex}).`, chalk.yellowBright, 'warning')
173
- logger.unsetAsActiveLogger()
174
- }
175
-
176
- const smallestCostDirection = argIndexOfMinimumOf3(upCost, leftCost, upAndLeftCost)
177
-
178
- if (smallestCostDirection == 1) {
179
- rowIndex = upRowIndex
180
- columnIndex = upColumnIndex
181
- } else if (smallestCostDirection == 2) {
182
- rowIndex = leftRowIndex
183
- columnIndex = leftColumnIndex
194
+ // In that case:
195
+ //
196
+ // If there are rows above
197
+ if (upRowIndex >= 0) {
198
+ // Move upward
199
+ rowIndex = upRowIndex
200
+ } else if (leftColumnIndex >= 0) {
201
+ // Otherwise, move to the left
202
+ columnIndex = leftColumnIndex
203
+ } else {
204
+ // Since we know that either columnIndex > 0 or rowIndex > 0,
205
+ // one of these directions must be available.
206
+ // This error should never happen
207
+
208
+ throw new Error(`Unexpected state: columnIndex: ${columnIndex}, rowIndex: ${rowIndex}`)
209
+ }
184
210
  } else {
185
- rowIndex = upAndLeftRowIndex
186
- columnIndex = upAndLeftColumnIndex
211
+ // Choose the direction with the smallest cost
212
+ const smallestCostDirection = argIndexOfMinimumOf3(upCost, leftCost, upAndLeftCost)
213
+
214
+ if (smallestCostDirection == 1) {
215
+ // Move upward
216
+ rowIndex = upRowIndex
217
+ // The upper column index stays the same
218
+ } else if (smallestCostDirection == 2) {
219
+ // Move to the left
220
+ rowIndex = leftRowIndex
221
+ columnIndex = leftColumnIndex
222
+ } else {
223
+ // Move upward and to the left
224
+ rowIndex = upAndLeftRowIndex
225
+ columnIndex = upAndLeftColumnIndex
226
+ }
187
227
  }
188
228
  }
189
229
 
@@ -38,12 +38,12 @@ export async function alignUsingDtw(
38
38
  let relativeCenters: number[] | undefined
39
39
 
40
40
  for (let passIndex = 0; passIndex < windowDurations.length; passIndex++) {
41
+ const granularity = granularities[passIndex]
41
42
  const windowDuration = windowDurations[passIndex]
42
- const granularity = resolveAutoGranularityIfNeeded(granularities[passIndex], rawAudioDuration)
43
43
 
44
44
  logger.logTitledMessage(`\nStarting alignment pass ${passIndex + 1}/${windowDurations.length}`, `max window duration: ${windowDuration}s, granularity: ${granularity}`, chalk.magentaBright)
45
45
 
46
- const mfccOptions = extendDefaultMfccOptions({ ...getMfccOptionsForGranularity(granularity, rawAudioDuration), zeroFirstCoefficient: true }) as MfccOptions
46
+ const mfccOptions = extendDefaultMfccOptions({ ...getMfccOptionsForGranularity(granularity), zeroFirstCoefficient: true }) as MfccOptions
47
47
 
48
48
  framesPerSecond = 1 / mfccOptions.hopDuration!
49
49
 
@@ -67,7 +67,7 @@ export async function alignUsingDtw(
67
67
  }
68
68
  }
69
69
 
70
- logger.start('Align MFCC features using DTW')
70
+ logger.start('Align reference and source MFCC features using DTW')
71
71
  const dtwWindowLength = Math.floor(windowDuration * framesPerSecond)
72
72
 
73
73
  let centerIndexes: number[] | undefined
@@ -458,7 +458,7 @@ export async function createAlignmentReferenceUsingEspeakForFragments(fragments:
458
458
  progressLogger.start("Load espeak module")
459
459
  const Espeak = await import("../synthesis/EspeakTTS.js")
460
460
 
461
- progressLogger.start("Create alignment reference with eSpeak")
461
+ progressLogger.start("Synthesize alignment reference with eSpeak")
462
462
 
463
463
  const result = await Espeak.synthesizeFragments(fragments, espeakOptions)
464
464
 
@@ -476,7 +476,7 @@ export async function createAlignmentReferenceUsingEspeakForFragments(fragments:
476
476
  export async function createAlignmentReferenceUsingEspeak(transcript: string, language: string, plaintextOptions?: API.PlainTextOptions, customLexiconPaths?: string[], insertSeparators?: boolean) {
477
477
  const logger = new Logger()
478
478
 
479
- logger.start('Create alignment reference with eSpeak')
479
+ logger.start('Synthesize alignment reference with eSpeak')
480
480
 
481
481
  const synthesisOptions: API.SynthesisOptions = {
482
482
  engine: 'espeak',
@@ -544,25 +544,9 @@ function getMappedFrameIndexForPath(referenceFrameIndex: number, compactedPath:
544
544
  return mappedFrameIndex
545
545
  }
546
546
 
547
- function resolveAutoGranularityIfNeeded(granularity: DtwGranularity, audioDuration: number) {
548
- if (granularity != 'auto') {
549
- return granularity
550
- }
551
-
552
- if (audioDuration < 60) {
553
- return 'high'
554
- } else if (audioDuration < 60 * 10) {
555
- return 'medium'
556
- } else {
557
- return 'low'
558
- }
559
- }
560
-
561
- function getMfccOptionsForGranularity(granularity: DtwGranularity, audioDuration: number) {
547
+ function getMfccOptionsForGranularity(granularity: DtwGranularity) {
562
548
  let mfccOptions: MfccOptions
563
549
 
564
- granularity = resolveAutoGranularityIfNeeded(granularity, audioDuration)
565
-
566
550
  if (granularity == 'xx-low') {
567
551
  mfccOptions = { windowDuration: 0.400, hopDuration: 0.160, fftOrder: 8192 }
568
552
  } else if (granularity == 'x-low') {
@@ -595,4 +579,4 @@ export type CompactedPathEntry = {
595
579
  first: number, last: number
596
580
  }
597
581
 
598
- export type DtwGranularity = 'auto' | 'xx-low' | 'x-low' | 'low' | 'medium' | 'high' | 'x-high'
582
+ export type DtwGranularity = 'xx-low' | 'x-low' | 'low' | 'medium' | 'high' | 'x-high'