* Distribute timestamps evenly across zero-duration word clusters. * Whisper sometimes assigns identical start/end to sequences of words, * making karaoke highlights flash through them instantly. * * Also handles malformed timestamps where start > end — these are treated * the same as zero-dura
(words: Word[])
| 96 | * the same as zero-duration and get interpolated from surrounding words. |
| 97 | */ |
| 98 | function interpolateZeroDuration(words: Word[]): void { |
| 99 | for (let i = 0; i < words.length; i++) { |
| 100 | const wi = words[i]; |
| 101 | if (!wi || wi.start < wi.end) continue; |
| 102 | let j = i; |
| 103 | while (j < words.length) { |
| 104 | const wj = words[j]; |
| 105 | if (!wj || wj.start < wj.end) break; |
| 106 | j++; |
| 107 | } |
| 108 | const clusterLen = j - i; |
| 109 | const prev = i > 0 ? words[i - 1] : undefined; |
| 110 | const prevEnd = prev ? prev.end : wi.start; |
| 111 | const nextWord = j < words.length ? words[j] : undefined; |
| 112 | const nextStart = nextWord ? nextWord.start : prevEnd + clusterLen * 0.3; |
| 113 | const span = nextStart - prevEnd; |
| 114 | const perWord = span / clusterLen; |
| 115 | for (let k = i; k < j; k++) { |
| 116 | const wk = words[k]; |
| 117 | if (!wk) continue; |
| 118 | wk.start = round3(prevEnd + (k - i) * perWord); |
| 119 | wk.end = round3(prevEnd + (k - i + 1) * perWord); |
| 120 | } |
| 121 | i = j - 1; |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | function parseWhisperCpp(data: Record<string, unknown>): Word[] { |
| 126 | const words: Word[] = []; |
no test coverage detected