瀏覽器裡的抓譜工具 · 音訊不上傳
先給你一個會改的答案,
再給你定稿
錄音當下就把和弦與音高推上畫面,看得到它在跟著動;停止之後拿整段波形重跑一次,用更準的結果覆蓋掉它。
兩版不一樣是正常的——所以工具自己會講出哪一版是暫定的,以及重判改了什麼。
為什麼要分兩段
因為「馬上看到」跟「判得準」是兩個互相打架的需求。
即時 · 暫定
它看不到未來。聽到新的證據就會改答案,短錄音甚至判不出調號—— 因為它真的還沒聽夠。
價值不在準,在於你看得到它在跟著你彈的東西動。
重判 · 定稿
停止之後拿整段波形重跑。先跑完全部拿到最終調號,再用那個調號一次算對級數, 不必邊聽邊修。
兩條路共用同一套 DSP,差別只有串流餵與整段重跑。
真正難的不是演算法,是畫面。 即時結果本來就會跳,這在演算法上完全正確,在畫面上卻是災難: 使用者不知道哪一版是暫定的,只會覺得「這個工具很不穩」。
所以整個介面只用一條規則區分兩版:虛線+琥珀色=暫定、 實線+綠色=定稿。 重判完成後那一行會直接寫出差異——「調號跟即時一樣(C 大調)· 和弦 7 段 → 7 段」。 使用者看到答案自己變了,要能分辨這是重判修正了它,而不是工具在亂跳。
四個畫面
全部是真的跑出來的:合成的音進去,這些是出來的東西。
分析結果不等於譜
數字全對、記號寫錯,樂手一眼就不信這張譜了。
小節裡不能有五拍
原本「放不下就整個推到下一小節」,四四拍的小節因此塞進五拍—— 這種錯樂手數一下就發現,而且它讓整張譜失去可信度。
改成把跨過小節線的音切開、再用連結線接回去。弧線用 SVG 畫,不用 ⌒ 字元:
靠字型碰運氣會變成豆腐方塊。
空隙不等於休止符
切音本來就會在音跟音之間留空隙——起音偵測靠它才切得開重複音。那不是休止。 門檻定在 0.4 拍,低於它一律當成正常斷句。
還有一條容易順手寫錯的:休止符被小節線切開時不接連結線。 沒有東西在響,切開就只是兩個休止。
推不出來就不要編號
小節線是靠「和弦變化通常落在小節開頭」推的。支持度不夠就不畫,退成「依和弦變化分段」—— 而那些格子不是小節,所以連小節編號也不標。
少講可以,講錯不行。同一條原則也讓歌詞維持人工貼上,不做語音辨識。
量出來的幾個數字
每一個都對得上一支跑得起來的測試。
9ms
即時每秒訊號的
CPU 成本(上限 1000)
0
丟棄的幀
餵 4096 或 16384 都一樣
7.8s
8 秒錄音的即時累積
差 0.2 秒判不出調號
0
網路請求
與外部依賴
第三個數字是這支工具最喜歡的一個:8 秒的錄音,即時側累積到 7.8 秒、判不出調號(正確,它真的還沒聽夠), 重判側整段跑完給出 C 大調(正確,它已經聽完了)。同一段程式碼、兩個呼叫端,兩行同時是對的—— 所以測試不是斷言兩版一致,而是斷言它們該不一致的時候真的不一致。
技術架構
DSP
4096 點 FFT / hop 1024
峰值取樣+拋物線內插
12 維音級 × 84 個和弦模板
兩條管線
chroma 要頻率解析度
YIN 要時間解析度
需求相反,所以分開跑
即時
ScriptProcessorNode
餵進來的塊自己切 4096
環形緩衝滿了會安靜丟資料
驗證
3 支 node 端到端測試
Playwright 走 5 段流程
錄音用假麥克風真的走過
整套訊號處理沿用 Ear Notes(眼鏡上實測過的那一套),一行沒改就換了平台—— 純運算模組不碰 I/O 的價值,在移植的時候才兌現。