cut-rough
機械式緊縮:空白片段、填充詞、NG 片段、拼接衛生處理。這裡的所有操作都是可逆的,且每個決定都有來源記錄。
引數 $project 是專案代號;完整請求在 $ARGUMENTS 中。空的 $-占位符表示該技能是自動觸發而非透過斜線指令呼叫:從工作階段上下文的 PIPELINE_STATE 取得專案,並告知你選了哪一個。動詞名稱遵循建構合約;若發生衝突,以 python engine/ve.py --help 為準。
前置條件,及其嚴格原因
先執行 cut-story。如果 story.blocks 遺失,請告知並提議執行 cut-story,而非繼續進行——對即將被整批捨棄的素材進行機械式裁切,只會浪費時間在沒人會聽到的重拍與填充詞偵測上。
如果使用者明確要求對所有內容進行機械式處理(例如「einfach nur die Ähms raus」),這是合理的。執行它,並說明沒有進行任何內容選擇。
幀規則
裁切點必須對齊整幀。這不是為了美觀——偏離幀格網格的裁切點會讓 concat 濾鏡每次累積 8.33 毫秒的音訊/視訊偏移,150 次裁切後偏移 1250 毫秒。對齊後,同樣 150 次裁切產生的偏移恰好為 0 個取樣。
in_frame = floor(t_in * fps + 0.5) # 四捨五入向上 -> 平局時向後靠
out_frame = ceil (t_out * fps - 0.5) # 四捨五入向下 -> 平局時向前靠
if out_frame <= in_frame: out_frame = in_frame + 1
out 是獨佔的——ffmpeg 將 trim=end 實現為嚴格的 <,而半開區間 [in, out) 正是使拼接精確的關鍵。若 OUT 的捨入方向錯誤,每個片段都會變成一幀長;200 次裁切會產生 6.7 秒的偏移,導致明顯的同步錯誤。
永遠不要手動執行此算術。vedit/timebase.py 負責處理:使用 snap_in / snap_out。
步驟 1 - 執行各趟處理
python engine/ve.py cut $project
對扁平化的詞彙列表執行四趟處理,然後進行對齊:
- 靜音分級。 低於
min_gap的間隙永不裁切。高於此值時,保留的停頓長度會根據前一個字的標點符號而變化:.?!為 1.5 倍(下限 0.40 秒),,;:為 1.0 倍,句中分句為 0.7 倍(下限 0.20 秒),話題轉換為 2.0 倍(下限 0.60 秒)。統一的停頓長度是機器編輯最明顯的特徵——耳朵期望在句號之後有更長的呼吸停頓。 - 填充詞分級。 見下方說明。
- 重拍偵測。 重複的 n-gram,其中後來的版本取代了先前的版本。
- 拼接衛生。 填充、交叉淡入淡出、零交叉點對齊、裁切密度限制。
步驟 2 - 填充詞分級,以及不可自動化的那一級
- A 級 - 非詞彙(
äh ähm hm mh uh um er):自動移除。沒有人需要這些。 - B 級 - 詞彙填充詞(
weißt du, sozusagen, quasi, im Prinzip, you know, I mean):自動移除,但需加上保護條件——不在句首,且移除後不會留下低於min_clip的片段。 - C 級 - 情態小品詞:
also halt eben ja mal doch schon eigentlich genau,以及英文的like just well right so basically actually。僅標記。永不自動移除。
C 級是自動編輯工具毀掉德語演講的地方。doch、eben、halt 和 ja 承載著態度和話語結構;移除它們並不會讓講者聽起來更緊湊,而是像機器翻譯。如果使用者無論如何要求移除,請執行——但先說明一次其代價,之後就不再提及。
偵測訊號順序:詞彙匹配,然後 duration/char >= 0.12 秒,接著兩側間隙 >= 0.10 秒,最後能量在周圍語音 6 dB 以內。
切勿將 WhisperX score 當作填充詞訊號。 它衡量的是對齊品質,而非猶豫程度。低分數集中在罕見詞和專有名詞上——正好與填充詞相反。僅將其用作時間戳可靠性標記:score < 0.35 會將填充寬度增加 60 毫秒,並禁止在該詞內部進行裁切。
步驟 3 - 預設值及其狀態
| 參數 | 預設值 | 說明 |
|---|---|---|
pad_in | 0.08 秒 | /p t k b d g/ 的塞音除阻前閉合期 |
pad_out | 0.10 秒 | /t k s/ 的釋放會超過對齊器的 end |
keep_pause | 下限 0.18 秒 | 在空白處保留的呼吸間隙 |
min_gap | 0.55 秒 | 低於此值,永不裁切 |
min_removal | 0.20 秒 | 更短的移除不值得一次拼接 |
max_cut_density | 每 1.5 秒 1 次 | 跳剪疲勞 |
min_clip | 0.35 秒 | 更短的保留片段會合併到相鄰片段 |
| 交叉淡入淡出 | 20 毫秒(最小 10,最大 40) | 同一錄影使用 curve=tri,跨錄影使用 qsin |
| 零交叉搜尋 | +/-5 毫秒(240 個取樣) | 僅音訊,絕不移動視訊 |
這些是研究推導值,未經感知測試。 當 project.json 中的 "calibrated": false 時,每當你為某個裁切辯護時,都必須說明此事。不要將未經校準的門檻呈現為調整過的值。工作階段的掛鉤會因此,在每個工作階段都顯示這個標記。
零交叉點對齊值得理解:在非零交叉點處進行硬裁切會產生一個比訊號自然最大值大 29.5 倍的取樣步進——一個可聽見的喀噠聲。同時使用零交叉點對齊和 20 毫秒的交叉淡入淡出可將其恢復到基線。兩者皆用。對齊僅移動音訊,絕不移動視訊;子幀的視訊偏移是不可表示的。
步驟 4 - J/L 剪輯
4-12 幀是實用範圍;預設值為 6。
拒絕任何兩側共享 source_id 且均未被 B-roll 或全幀疊加覆蓋的 J/L 剪輯。 在可見的嘴巴上,這不是 J 剪輯,而是唇形同步錯誤,並且會被解讀為技術故障而非編輯技巧。
步驟 5 - 寫入片段
片段寫入 segments[],以 src_in_frames(包含)和 src_out_frames(獨佔)為唯一權威。每個片段還需要:
enabled- 被拒絕的素材設定為false並附上理由,絕不刪除。 每個自動決定都必須能透過翻轉一個布林值來反轉。這個屬性使積極的預設值保持安全。order- 明確的輸出順序,在啟用的片段上緊密排列。provenance-stage(故事層級的捨棄為content,silence,filler,retake)、rule、confidence、applied_by,以及觸發該決策的測量值(original_gap_s)。來源記錄讓使用者可以說「撤銷所有 B 級移除」。word_range和去正規化的text。文字僅供閱讀,從不具有權威性——如果它與word_range衝突,以詞彙範圍為準。
時間軸位置是衍生的,從不儲存。它們是在載入時對啟用片段進行累加求和後寫入 _computed 的結果。永遠不要手動寫入 _computed。
將 stage 設定為 "rough_cut"。
步驟 6 - 像編輯師而非像腳本那樣報告
報告內容:原始時長、裁切後時長、移除百分比、裁切次數、裁切密度(每分鐘裁切數),以及每個來源規則的計數。然後標記任何看起來像過度裁切的情況:一段中每秒超過一次裁切的區域、任何接近 min_clip 的保留片段,以及被標記但未被處理的 C 級填充詞數量。
提供三個下一步選項:預覽某個片段、調整某個門檻,或進入圖形/字幕階段。
失敗模式
| 症狀 | 原因 | 修復 |
|---|---|---|
| 每個片段都只有一幀 | OUT 向上而非向下捨入 | 使用 snap_out;切勿內聯計算該數學式 |
| 拼接處有明顯喀噠聲 | 跳過零交叉點對齊 | 對齊並加上 20 毫秒交叉淡入淡出 |
| 語音聽起來破碎且急促 | 統一保留的停頓長度 | 套用標點符號縮放表 |
| 講者聽起來像機器人且不像德語 | 移除了 C 級 | C 級僅標記;透過來源記錄恢復 |
| 裁切落在詞語中間 | 轉錄文本為第 3 級(BPE 詞元) | 重新轉錄;詞元時間戳不可作為裁切依據 |
| 整個視訊的音訊/視訊偏移增加 | 裁切點未對齊幀 | 對齊每個裁切點,然後重新執行 QC 檢查 (a) |