靜態歌詞最令人挫折的時刻,不是它沒有文字,而是歌正在播,文字卻像一本沒有書籤的書。你看得到全部內容,卻不知道現在該看哪裡。

常見的直覺是:既然有麥克風,就做 ASR,把聽到的歌詞直接對上文字。但歌唱辨識比口述聽寫難得多,還帶著語言、音質、成本與隱私的問題。更重要的是,它把一個本來可以拆小的問題,一開始就當成「必須選出唯一正解」。

這次在 LyricGlass 裡,我們先處理一個更務實的版本:已知歌曲總長度、目前播放位置,以及一份可能不完整的靜態歌詞時,能不能先把畫面帶到大致正確的三行?這不是產生同步 LRC,也不是用來控制播放;它是讓單一裝置上的歌詞顯示,在沒有時間戳時仍然有可用的視覺定位。

先承認:靜態歌詞本身沒有時間

只有純文字,無法直接推導「第 78 秒正在唱哪一行」。缺少的不是演算法技巧,而是時間證據。

不過,歌曲進度本身是一條很弱、卻真實存在的訊號。假設歌曲播到 40%,把這個比例投射到正規化後的歌詞文字長度,就能取得一個大致中心點;再從中心點取回一段足夠寬的文字窗口。這是一個粗估,不是假裝知道每一行的時間戳。

我把窗口寬度寫成一個常數 c:

窗口長度 = ceil(c × 正規化後歌詞總長度)

c 越大,命中機率越高,但候選範圍也越寬;若 c = 1,答案永遠是整份歌詞,當然不會錯,也完全沒有價值。因此真正的問題不是「怎樣拿滿分」,而是「最小而仍可靠的窗口要多大」。

把漂亮的假設,改成可被推翻的研究

第一版只有五首樣本,c = 0.465 剛好能讓所有可評估的歌詞行落進窗口。這個數字看起來精準,卻沒有資格成為產品答案:同一批歌既拿來選常數,又拿來評分,本質上是在對樣本校準。

所以實驗擴展到 30 首不同結構的 LRCLIB 記錄:9 種語言、30 個研究分層、共 1,401 個歌詞 occurrence。樣本只保存研究所需的 metadata;歌詞正文不寫進研究產物。同步歌詞只作為隱藏答案,靜態 plainLyrics 才是演算法真正可見的輸入。

這裡另一個容易被忽略的規則是:不能假設靜態歌詞完整。缺少一段主歌、重複句被省略、拆行或合行,都是真實資料條件,而不是可以悄悄排除的髒資料。因此評估把無法對齊的 occurrence 直接算作 miss,另外報告可對齊覆蓋率;不讓「只挑容易對齊的行」把命中率美化掉。

30 首樣本的曲線讓原本的數字失效了:追求同批樣本的最小滿分窗口,需要 c = 0.4831;但 c = 0.4 已命中 1,373 / 1,401 個 occurrence,整體為 98.0%,而候選窗口比那個滿分值小約 17%。

為什麼最後選 0.4,而不是 0.4831

0.4831 是一個很好的研究觀察:樣本一擴大,舊的五首校準值就不再成立。但它不是一個好的產品信念,因為它正是為這 30 首樣本的滿分邊界而長出來的數字。

0.4 反而比較誠實。它是一個容易理解、刻意保留餘裕的粗定位先驗:回傳約 40% 的歌詞,讓後續邏輯只在這個區域內工作。它不聲稱能選出唯一歌詞行,也不把範圍外的 2% 漏失藏起來。

真正重要的轉折是:產品不必把「唯一行」當成第一版的驗收條件。對歌詞顯示而言,把正確區域自動捲進可見範圍,使用者往往已經能立刻接回歌曲。這把問題從「替使用者做完判斷」,改成「先消除大部分搜尋成本」。

從研究數字,到真實畫面

Lab v0 只會在最終歌詞文件是 UNSYNCED、有 plainLyrics、且歌曲時長與播放位置都已知時啟用。它不根據 Apple Music、Musixmatch、Netease 或 LRCLIB 的來源身分改變算法;來源不是完整度的證據。