Lapel-Sync
Kameraclips plus eine separate Recorderspur hinein, eine Lapel-Scheibe pro Clip und eine synchronisierte Resolve-Timeline heraus. Erstmals getestet bei einem Live-Talk: 5 Kameraclips gegen eine 59-minütige Recorderspur. Jeder Clip landete innerhalb von 20 ms, deutlich unter einem Frame.
Warum es wichtig ist: Bei einem Live-Event befindet sich das Kameramikrofon im Raum. Seine Audioqualität ist unbrauchbar, und das Lapel ist die einzige saubere Tonquelle. Nach diesem Skill sind <work>/<clip>.wav und <clip>.json das Lapel-Audio, sodass paper-edit und video-reels sauberen Ton und saubere Transkripte lesen, ohne zu wissen, dass sich etwas geändert hat.
Einrichtung: Diese Felder ausfüllen
| Platzhalter | Bedeutung | Beispiel |
|---|---|---|
<FOOTAGE_DRIVE> | Ort des Rohmaterials, ein Ordner pro Video, Recorder-Dateien in einem audio/-Unterordner | /Volumes/MyWorkDrive |
<SYNC_OUT> | Ziel für Lapel-Scheiben in voller Qualität | ~/Movies/<video>-sync |
<LUT_PATH> | Optionaler LUT, relativ zum Resolve-LUT-Ordner | MyLuts/SLog3-to-709.cube |
WHISPER_MODEL (env) | Whisper.cpp-Modell-Datei | Standard: ~/.cache/whisper-models/ggml-small.en.bin |
Voraussetzungen: ffmpeg, ffprobe, whisper-cli (whisper.cpp), Python 3 (numpy und scipy werden automatisch in ~/.cache/lapel-sync-venv installiert), DaVinci Resolve mit einem Resolve MCP-Server, der execute_resolve_code bereitstellt, und das paper-edit-Skill (für ensure_resolve.sh).
Ausführen
Arbeitsverzeichnis: das Session-Scratchpad, z.B. <scratchpad>/<video>. Das Skill-Verzeichnis befindet sich unter .claude/skills/lapel-sync. Qualitativ hochwertige Scheiben gehen nach <SYNC_OUT>.
Zsh-Falle: In einer Inline-Shell wird $ss:end_sample als ss gefolgt vom Modifikator :e interpretiert, der den Rest stillschweigend frisst. Immer ${ss} schreiben.
1. Das Material und das Audio finden
Ein Ordner pro Video auf <FOOTAGE_DRIVE>. Die Recorder-Dateien liegen in einem audio/-Unterordner:
find "<FOOTAGE_DRIVE>" -maxdepth 4 -iname "*<keyword>*" -not -path "*/.*"
ls "<folder>" "<folder>/audio"
Ein Tascam DR-10L zum Beispiel schreibt 000_YYMMDD.wav, 001_..., aufgeteilt alle 15 Minuten, plus _D-Zwillinge. Die _D-Dateien sind der Dual-Record-Sicherheitstrack, etwa 20 dB leiser. Die Hauptdateien verwenden; _D für das Flicken übersteuerter Stellen aufheben.
2. Die Recorder-Dateien zusammenfügen
bash <skill>/scripts/stitch.sh "<folder>/audio" "<SYNC_OUT>/lav-master.wav"
Es liest die BWF time_reference (Samples seit Mitternacht) jeder Datei und prüft, dass jede Datei genau dort beginnt, wo die letzte endete. Wenn sie sample-kontinuierlich sind, handelt es sich um eine verlustfreie Stream-Kopie. Bei einer Lücke wird entsprechend Stille eingefügt, sodass der Master auf der Wanduhrzeit bleibt, und es wird mit der Größe gewarnt.
3. Beide Seiten transkribieren
bash <skill>/scripts/transcribe_sources.sh "<folder>" "<SYNC_OUT>/lav-master.wav" "<work>"
Alles landet in <work>/sync/, außerhalb von <work> selbst, damit paper-edit's sentences.py es nie liest. Das Kamera-Audio wird zuerst stark angehoben (highpass=f=120,volume=40dB,alimiter, 16k mono), da es oft um -65 dB liegt. Bereits Erledigtes wird übersprungen, also kann das Skript beliebig oft erneut ausgeführt werden.
Das Lapel benötigt etwa 8 Minuten pro Stunde Audio. Im Hintergrund laufen lassen. Whisper streamt Wortzeitstempel in <work>/sync/lav.log, und align.py liest dieses Log, wenn lav.json noch nicht geschrieben ist, sodass die Ausrichtung beginnen kann, bevor das Lapel fertig ist. Clips, die den transkribierten Punkt überschreiten, melden lediglich "only N word matches"; nach Abschluss erneut ausführen.
4. Ausrichten
python3 <skill>/scripts/align.py "<work>"
Schreibt <work>/sync/sync.json. Beim ersten Durchlauf wird ~/.cache/lapel-sync-venv mit numpy und scipy erstellt (system python3 hat oft keines von beiden); danach führt es sich selbst innerhalb dieser Umgebung aus.
So funktioniert es, damit Sie der Ausgabe vertrauen oder sie hinterfragen können:
- Zuerst Wörter. Einzigartige 4-Wort-Sequenzen, die in beiden Transkripten vorkommen. Offset = Lapel-Zeit minus Kamera-Zeit. Hunderte von Treffern pro Clip; dichteste Gruppe, innerhalb von 0,5 s behalten, Median. Gut auf etwa 0,1 s.
- Wellenform-Verfeinerung. Jeder 20-s-Abschnitt des Kamera-Audios (6-s-Abschnitte bei Clips unter 40 s), bandpassgefiltert 300-3000 Hz bei 8 kHz, wird innerhalb von ±0,6 s des Wort-Offsets mit dem Lapel kreuzkorreliert. Spitzen über dem 6-fachen Median zählen. Abschnitte innerhalb von 20 ms voneinander stimmen überein; bei Clips über 5 Minuten liefert eine lineare Anpassung Offset plus Drift.
Jede gedruckte Zeile lesen: Offset, übereinstimmende Abschnitte, Streuung, Drift. Jeder als CHECK BY EAR markierte Clip (weniger als 3 übereinstimmende Abschnitte) kann bis zu 0,1 s abweichen; in Resolve überprüfen, bevor Sie ihm vertrauen. Eine Drift von mehr als einem Frame über einen Clip wird ebenfalls gemeldet.
5. Lapel pro Clip schneiden
python3 <skill>/scripts/slice.py "<work>" "<SYNC_OUT>/lav-master.wav" "<SYNC_OUT>"
Für jeden Clip schreibt es:
<SYNC_OUT>/<clip>_lav.wav: 24-Bit, Quellrate, sample-exakt zur Länge des Clips, sodass Lapel-Zeit gleich Clip-Zeit ist<work>/<clip>.wav(16k mono) und<work>/<clip>.json(Whisper der Scheibe): waspaper-editundvideo-reelslesen<work>/clips.ndjsonmit einem"audio"-Feld, das auf die Scheibe verweist
Wenn der Recorder für einen Clip zu spät begann oder zu früh endete, wird dieser Teil mit Stille gefüllt und es wird gewarnt. Whisper auf den Scheiben benötigt ein paar Minuten; führen Sie danach nicht auch paper-edit's transcribe.sh aus (es würde ohnehin überspringen, da die JSONs existieren).
6. Auf Übersteuerung prüfen
slice.py zählt Samples pro Scheibe bei oder über 0,99 Vollaussteuerung und gibt die längste Serie aus. Hunderte kurzer Serien (zig Samples) sind normalerweise der Limiter des Recorders, der Spitzen abfängt, keine Verzerrung. Nur wenn Stellen zum Anhören aufgelistet werden (Serien von 1 ms oder mehr), oder der Benutzer sagt, es klinge dumpf, diese Momente aus dem _D-Zwilling flicken (gleiche Zeit, etwa 20 dB anheben).
7. In Resolve bauen
bash .claude/skills/paper-edit/scripts/ensure_resolve.sh
<work>/sync/params.json schreiben:
{"project":"<Video> - YYYY-MM-DD","work":"<work>","name":"Synced - Full Talk", "lut":"<LUT_PATH>"}
lut ist optional; weglassen, wenn keines verwendet wird. Dann ein execute_resolve_code-Aufruf:
PARAMS = "<work>/sync/params.json"
exec(open("<skill>/scripts/resolve_sync_timeline.py").read())
Es erstellt das Projekt (oder öffnet es, wenn der Name existiert), importiert die Kameraclips und die Lapel-Scheiben und baut Synced - Full Talk: jeder Clip auf V1 als reines Bild (mediaType: 1), seine Scheibe auf A1 als reiner Ton (mediaType: 2), gleiche In- und Out-Frames. recordFrame = Timeline-Start + round((offset - frühester Offset) * 24000/1001), sodass die echten Lücken zwischen den Clips erhalten bleiben. LUT auf Node 1. Kamera-Scratch-Audio bleibt aus. Timeline ist 1080x1920, wenn die Clips hochkant sind, sonst 1920x1080 (mit width/height überschreibbar). Wenn der Timeline-Name bereits vergeben ist, wird v2 angehängt, anstatt die vorhandene zu ändern.
Der Benutzer sollte ein paar Stellen mit deutlichen Konsonanten (ein "p" oder "t") in jedem Clip überprüfen. Das ist der echte Test.
8. Übergeben
Dem Benutzer mitteilen, dass die Timeline existiert, die Clip-Zahlen aus Schritt 4 in einer Zeile pro Clip, alles Markierte und das Urteil zur Übersteuerung. Dann weitermachen: paper-edit (den Transkriptionsschritt überspringen; <work> ist bereits eingerichtet) oder video-reels.
Wissenswerte Fakten
- Wellenformkorrelation allein ist gescheitert. Die Korrelation über ganze Clips und 20-s-Abschnitte über ein breites Fenster hinweg lieferte inkonsistenten Müll, wenn das Kameramikrofon bei etwa -65 dB Mittelwert in einem halligen Raum lag. Zuerst Wörter, dann eine schmale Wellenformsuchen – das funktioniert.
- Die Kamerauhr ist oft falsch. Im Test lag die Kamera etwa 149 s hinter dem Recorder. Einige Kameras (z.B. Sony) schreiben
creation_timein UTC (endet mitZ), während die BWFtime_referenceeines Recorders lokal ist.creation_timebestenfalls als groben Hinweis behandeln. Der Wortabgleich benötigt sie nicht. - Drift ist vernachlässigbar bei der getesteten Hardware: etwa 0,5 ms/min, 11 ms über einen 21-minütigen Clip. Keine Zeitdehnung erforderlich. Kurze Clips melden 0, da eine Anpassung über wenige Sekunden nur Rauschen ist.
- Das erneute Ausführen der Skripte reproduzierte handgeschnittene Scheiben innerhalb von 6 ms.
- Hochkantmaterial: z.B. eine Sony, die hochkant filmt, zeichnet 4K 23.976 mit einer rotation=90-Anzeigematrix auf. Resolve liest es als 2160x3840, ohne dass eine Rotationskorrektur erforderlich ist.
- Beispiel-Recorder, Tascam DR-10L: 48k 24-Bit-Mono-BWF, neue Datei alle 15 Minuten,
_D-Sicherheitszwilling etwa 20 dB leiser. paper-edit'sresolve_build.pynimmt den Ton immer noch aus der Kameradatei. Bei einem Lapel-Dreh tragen seine Timelines das Scratch-Audio der Kamera, bis es lernt, das"audio"-Feld zu lesen. Bis dahin den Benutzer informieren oder A1 mit den_lav.wav-Scheiben neu verknüpfen; die Schnittpunkte sind identisch, da die Scheibenzeit gleich der Clipzeit ist.- Whisper läuft mit
-oj -ml 1 -sowfür Wortzeitstempel.
Verwandtes
paper-edit: der Rough Cut, nach diesem Skill ausführenvideo-reels: liestclips.ndjson; das"audio"-Feld teilt mit, den Ton von der Scheibe zu nehmen (falls installiert)