echogarden
Version:
An easy-to-use speech toolset. Includes tools for synthesis, recognition, alignment, speech translation, language detection, source separation and more.
102 lines • 3.86 kB
JavaScript
import { logToStderr } from '../utilities/Utilities.js';
const log = logToStderr;
export function alignLevenshtein(sequence1, sequence2, getSubstitutionCost) {
const rowCount = sequence2.length + 1;
const columnCount = sequence1.length + 1;
// Compute accumulated cost matrix
const accumulatedCostMatrix = computeAccumulatedCostMatrix(sequence1, sequence2, getSubstitutionCost);
// Find best path for the computed matrix
const path = computeBestPath(accumulatedCostMatrix);
// Best path cost is the bottom right element of the matrix
const pathCost = accumulatedCostMatrix[rowCount - 1][columnCount - 1];
return { path, pathCost };
}
function computeAccumulatedCostMatrix(sequence1, sequence2, getSubstitutionCost) {
const rowCount = sequence2.length + 1;
const columnCount = sequence1.length + 1;
const accumulatedCostMatrix = new Array(rowCount);
// Allocate rows and initialize first column
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
const newRow = new Float32Array(columnCount);
newRow[0] = rowIndex;
accumulatedCostMatrix[rowIndex] = newRow;
}
// Initialize first row
for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
accumulatedCostMatrix[0][columnIndex] = columnIndex;
}
// Fill out the rest of the matrix, go row by row
for (let rowIndex = 1; rowIndex < rowCount; rowIndex++) {
const previousRow = accumulatedCostMatrix[rowIndex - 1];
const currentRow = accumulatedCostMatrix[rowIndex];
for (let columnIndex = 1; columnIndex < columnCount; columnIndex++) {
const upAndLeft = previousRow[columnIndex - 1];
const up = previousRow[columnIndex];
const left = currentRow[columnIndex - 1];
const smallestCostDirection = argIndexOfSmallestOf3(upAndLeft, up, left);
let computedCostForCurrentElement;
if (smallestCostDirection == 1) {
const subtitutionCost = getSubstitutionCost(sequence1[columnIndex - 1], sequence2[rowIndex - 1]);
computedCostForCurrentElement = upAndLeft + subtitutionCost;
}
else if (smallestCostDirection == 2) {
computedCostForCurrentElement = up + 1;
}
else {
computedCostForCurrentElement = left + 1;
}
currentRow[columnIndex] = computedCostForCurrentElement;
}
}
return accumulatedCostMatrix;
}
function computeBestPath(costMatrix) {
const bestPath = [];
let rowIndex = costMatrix.length - 1;
let columnIndex = costMatrix[0].length - 1;
while (rowIndex > 1 || columnIndex > 1) {
bestPath.push({
source: columnIndex - 1,
dest: rowIndex - 1
});
if (rowIndex == 1) {
columnIndex -= 1;
continue;
}
if (columnIndex == 1) {
rowIndex -= 1;
continue;
}
const upAndLeft = costMatrix[rowIndex - 1][columnIndex - 1];
const up = costMatrix[rowIndex - 1][columnIndex];
const left = costMatrix[rowIndex][columnIndex - 1];
const smallestCostDirection = argIndexOfSmallestOf3(upAndLeft, up, left);
if (smallestCostDirection == 1) {
rowIndex -= 1;
columnIndex -= 1;
}
else if (smallestCostDirection == 2) {
rowIndex -= 1;
}
else {
columnIndex -= 1;
}
}
bestPath.push({
source: 0,
dest: 0
});
return bestPath.reverse();
}
function argIndexOfSmallestOf3(x1, x2, x3) {
if (x1 <= x2 && x1 <= x3) {
return 1;
}
else if (x2 <= x3) {
return 2;
}
else {
return 3;
}
}
//# sourceMappingURL=LevenshteinSequenceAlignment.js.map