領夾麥同步 是做什麼的?
將攝影機片段與獨立的錄音機音軌合併,輸出每個片段對應的領夾麥音軌以及一個同步完成的 Resolve 時間軸。最初在現場演講中測試:5 個攝影機片段對應一段 59 分鐘的錄音機音軌。每個片段同步誤差在 20 毫秒以內,遠低於一個影格。
為何重要: 在現場活動中,攝影機麥克風通常離講者很遠,其音訊無法使用。領夾麥是唯一乾淨的聲音來源。使用此技能後,<工作目錄>/<片段>.wav 和 <片段>.json 將代表領夾麥音訊,因此後續的 paper-edit 和 video-reels 技能可以直接讀取乾淨的音訊和逐字稿,無需知道其來源已改變。
設定:填寫以下項目
| 佔位符 | 說明 | 範例 |
|---|---|---|
<FOOTAGE_DRIVE> | 原始素材存放位置,每個影片一個資料夾,錄音機檔案在 audio/ 子資料夾中 | /Volumes/MyWorkDrive |
<SYNC_OUT> | 高品質領夾麥音軌的輸出目錄 | ~/Movies/<影片>-sync |
<LUT_PATH> | 可選的 LUT,路徑相對於 Resolve 的 LUT 資料夾 | MyLuts/SLog3-to-709.cube |
WHISPER_MODEL (環境變數) | whisper.cpp 模型檔案 | 預設 ~/.cache/whisper-models/ggml-small.en.bwav |
需求:ffmpeg、ffprobe、whisper-cli(whisper.cpp)、Python 3(numpy 和 scipy 會自動安裝到 ~/.cache/lapel-sync-venv),DaVinci Resolve 搭配一個暴露 execute_resolve_code 端點的 Resolve MCP server,以及 paper-edit 技能(用於 ensure_resolve.sh)。
執行
工作目錄:session 暫存區,例如 <暫存區>/<影片>。技能目錄為 .claude/skills/lapel-sync。高品質音軌輸出至 <SYNC_OUT>。
zsh 陷阱: 在內聯 shell 中,$ss:end_sample 中的 :e 會被解析為修飾符並無聲地吃掉文字。請始終使用 ${ss}。
1. 找到素材與音訊
每個影片在 <FOOTAGE_DRIVE> 上有一個資料夾。錄音機檔案位於 audio/ 子資料夾中:
find "<FOOTAGE_DRIVE>" -maxdepth 4 -iname "*<關鍵字>*" -not -path "*/.*"
ls "<資料夾>" "<資料夾>/audio"
例如,Tascam DR-10L 會寫入 000_YYMMDD.wav、001_...,每 15 分鐘分割一次,還有 _D 雙胞胎檔案。_D 檔案是雙錄製的安全音軌,音量約低 20 dB。請使用主檔案;保留 _D 檔案以備修補削波片段。
2. 拼接錄音機檔案
bash <技能目錄>/scripts/stitch.sh "<資料夾>/audio" "<SYNC_OUT>/lav-master.wav"
它會讀取每個檔案的 BWF time_reference(自午夜以來的樣本數),並檢查每個檔案的起始處是否與上一個檔案的結束處精準銜接。如果樣本連續,則進行無損串流複製。如果有間隙,則插入等長的靜音,使主檔案保持實際時間,並發出間隔長度的警告。
3. 轉錄兩方音訊
bash <技能目錄>/scripts/transcribe_sources.sh "<資料夾>" "<SYNC_OUT>/lav-master.wav" "<工作目錄>"
所有內容都輸出到 <工作目錄>/sync/,避免與 <工作目錄> 本身的內容混淆,這樣 paper-edit 的 sentences.py 就不會讀取到它。攝影機音訊會先被大幅增強(highpass=f=120,volume=40dB,alimiter,16k 單聲道),因為其音量通常約為 -65 dB。已處理過的項目會跳過,因此可以自由重新執行。
領夾麥音訊每小時約需 8 分鐘處理。可在背景執行。Whisper 會將單字時間戳串流輸出到 <工作目錄>/sync/lav.log,而 align.py 會在 lav.json 尚未寫入時讀取此日誌,因此對齊作業可以在領夾麥處理完成前開始。若片段超出已轉錄的範圍,只會回報「僅有 N 個單字匹配」;完成後重新執行即可。
4. 對齊
python3 <技能目錄>/scripts/align.py "<工作目錄>"
寫入 <工作目錄>/sync/sync.json。首次執行會建立包含 numpy 和 scipy 的 ~/.cache/lapel-sync-venv(系統的 python3 通常沒有這些套件);之後它會直接在該虛擬環境內重新執行。
運作原理,讓您可以信任或質疑其輸出:
- 先比對文字。 找出兩個轉錄稿中都出現的獨特 4 字詞組。偏移量 = 領夾麥時間減去攝影機時間。每個片段有數百個匹配;取最密集的叢集(保留在 0.5 秒內的),計算中位數。準確度約 0.1 秒。
- 再精細比對波形。 將每個攝影機音訊的 20 秒區塊(對於少於 40 秒的片段使用 6 秒區塊),以 300-3000 Hz 帶通濾波、8 kHz 取樣,與領夾麥音訊在文字偏移量 ±0.6 秒的範圍內進行交叉相關。峰值超過中位數的 6 倍即採用。彼此差異在 20 毫秒內的區塊視為一致;對於超過 5 分鐘的片段,會透過線性擬合得到偏移量與飄移量。
請閱讀它印出的每一行:偏移量、一致區塊數、分散程度、飄移量。任何標記為 CHECK BY EAR(一致區塊少於 3 個)的片段可能偏差達 0.1 秒;請在 Resolve 中手動檢查後再信任。飄移量超過一個影格的片段也會被標記。
5. 為每個片段裁切領夾麥音軌
python3 <技能目錄>/scripts/slice.py "<工作目錄>" "<SYNC_OUT>/lav-master.wav" "<SYNC_OUT>"
對於每個片段,它會寫出:
<SYNC_OUT>/<片段>_lav.wav:24 位元、原始取樣率、與片段長度樣本精準對齊,因此領夾麥時間等於片段時間<工作目錄>/<片段>.wav(16k 單聲道)和<工作目錄>/<片段>.json(該片段的 Whisper 轉錄):供 paper-edit 和 video-reels 讀取<工作目錄>/clips.ndjson,其中包含一個指向裁切音軌的"audio"欄位
如果錄音機延遲啟動或提前停止,則該部分會填入靜音並發出警告。對裁切後的音軌進行 Whisper 轉錄需要幾分鐘;請不要在之後也執行 paper-edit 的 transcribe.sh(它會因為 json 檔案已存在而跳過)。
6. 檢查削波
slice.py 會計算每個裁切音軌中達到大於等於 0.99 滿刻度(full scale)的樣本數,並印出最長的連續削波長度。數百個短暫的削波(數十個樣本)通常是錄音機的限制器在捕捉峰值,而非失真。只有在它列出需要聆聽的點(持續 1 毫秒或更長)時,或者使用者表示聽起來有破音時,才從 _D 雙胞胎檔案中修補那些瞬間(相同時間點,音量提升約 20 dB)。
7. 在 Resolve 中建立時間軸
bash .claude/skills/paper-edit/scripts/ensure_resolve.sh
寫入 <工作目錄>/sync/params.json:
{"project":"<影片名稱> - YYYY-MM-DD","work":"<工作目錄>","name":"Synced - Full Talk",
"lut":"<LUT_PATH>"}
lut 為選填;若不使用 LUT 則省略此欄位。然後執行一次 execute_resolve_code 呼叫:
PARAMS = "<工作目錄>/sync/params.json"
exec(open("<技能目錄>/scripts/resolve_sync_timeline.py").read())
它會建立專案(若名稱已存在則開啟),匯入攝影機片段與領夾麥音軌,並建立 Synced - Full Talk 時間軸:每個片段在 V1 軌道上僅顯示影像(mediaType: 1),其對應的裁切音軌在 A1 軌道上僅播放聲音(mediaType: 2),並設定相同的入點與出點影格。recordFrame = 時間軸起始點 + round((偏移量 - 最早偏移量) * 24000/1001),因此保留了片段之間的實際間隔。LUT 放在節點 1 上。攝影機的原始音軌則被保留不匯入。當片段是直向時,時間軸解析度為 1080x1920,否則為 1920x1080(可使用 width/height 覆寫)。如果時間軸名稱已被使用,則會加上 v2 後綴,而不是修改既有的時間軸。
請使用者檢查每個片段中幾個具有清晰子音(如 "p" 或 "t")的位置。這是最終的驗證步驟。
8. 交接
告知使用時間軸已建立、步驟 4 中每個片段的數據(一行一個)、任何被標記的片段以及削波檢查結果。然後繼續進行後續步驟:paper-edit(跳過其轉錄步驟,因為 <工作目錄> 已設定完成)或 video-reels。
值得記錄的事實
- 僅靠波形相關是失敗的。 在攝影機麥克風平均音量約 -65 dB 的迴音空間中,針對整個片段或 20 秒區塊進行大範圍的波形相關會產生不一致的垃圾結果。先比對文字,再進行狹窄範圍的波形搜尋,才是有效的方法。
- 攝影機時鐘經常不準。 在測試中,攝影機比錄音機慢了約 149 秒。有些相機(例如 Sony)會寫入 UTC 時間的
creation_time(結尾為Z),而錄音機的 BWFtime_reference是本地時間。最好只將creation_time視為粗略的參考。文字匹配不需要它。 - 飄移量可忽略。 在測試的設備上,飄移量約為 0.5 毫秒/分鐘,一個 21 分鐘的片段飄移 11 毫秒。無需進行時間拉伸。短片段會回報為 0,因為在幾秒鐘內進行擬合只會得到雜訊。
- 重新執行腳本可以將手動裁切的音軌複製到 6 毫秒以內。
- 直向素材: 例如,一台拍攝直向畫面的 Sony 相機會錄製解析度為 4K 23.976、顯示矩陣旋轉 90 度的影片。Resolve 會將其讀取為 2160x3840,無需進行旋轉修正。
- 範例錄音機,Tascam DR-10L: 48k 24 位元單聲道 BWF,每 15 分鐘建立新檔案,
_D安全雙胞胎檔案音量約低 20 dB。 - paper-edit 的
resolve_build.py仍會從攝影機檔案中擷取聲音。 在有領夾麥的拍攝場合中,其時間軸會包含攝影機的原始音軌,直到它學會讀取"audio"欄位為止。在那之前,請告知使用者,或將 A1 軌道重新連結到_lav.wav音軌;由於裁切時間等於片段時間,因此剪輯點是相同的。 - Whisper 使用
-oj -ml 1 -sow參數來取得單字時間戳。
相關技能
paper-edit:粗剪,在此技能之後執行video-reels:讀取clips.ndjson;"audio"欄位會告訴它從裁切音軌中取得聲音(如果已安裝)