Que fait Synchronisation lapel ?
Clips caméra + piste d'enregistreur séparée en entrée, une piste lapel par clip et une timeline Resolve synchronisée en sortie. Testé pour la première fois sur un talk en direct : 5 clips caméra contre une piste d'enregistreur de 59 minutes. Chaque clip a atterri à moins de 20 ms, bien en dessous d'une frame.
Pourquoi c'est important : lors d'un événement en direct, le micro de la caméra est à travers la pièce. Son audio est inutilisable, et le lapel est le seul son propre. Après cette compétence, <work>/<clip>.wav et <clip>.json sont le lapel, de sorte que paper-edit et video-reels lisent un audio propre et des transcriptions propres sans rien savoir du changement.
Configuration : à remplir
| Espace réservé | Ce que c'est | Exemple |
|---|---|---|
<FOOTAGE_DRIVE> | Où se trouvent les rushes bruts, un dossier par vidéo, fichiers d'enregistreur dans un sous-dossier audio/ | /Volumes/MyWorkDrive |
<SYNC_OUT> | Où vont les tranches lapel pleine qualité | ~/Movies/<video>-sync |
<LUT_PATH> | LUT optionnelle, relative au dossier LUT de Resolve | MyLuts/SLog3-to-709.cube |
WHISPER_MODEL (env) | Fichier modèle whisper.cpp | par défaut ~/.cache/whisper-models/ggml-small.en.bin |
Prérequis : ffmpeg, ffprobe, whisper-cli (whisper.cpp), Python 3 (numpy et scipy sont installés automatiquement dans ~/.cache/lapel-sync-venv), DaVinci Resolve avec un serveur MCP Resolve exposant execute_resolve_code, et la compétence paper-edit (pour ensure_resolve.sh).
Exécution
Répertoire de travail : le scratchpad de la session, par ex. <scratchpad>/<video>. Le répertoire de la compétence ci-dessous est .claude/skills/lapel-sync. Les tranches pleine qualité vont dans <SYNC_OUT>.
Piège zsh : dans un shell en ligne, $ss:end_sample interprète :e comme un modificateur et mange silencieusement le texte. Écrivez toujours ${ss}.
1. Trouver les rushes et l'audio
Un dossier par vidéo sur <FOOTAGE_DRIVE>. Les fichiers de l'enregistreur se trouvent dans un sous-dossier audio/ :
find "<FOOTAGE_DRIVE>" -maxdepth 4 -iname "*<keyword>*" -not -path "*/.*"
ls "<folder>" "<folder>/audio"
Un Tascam DR-10L, par exemple, écrit 000_YYMMDD.wav, 001_..., fractionné toutes les 15 minutes, plus des jumeaux _D. Les fichiers _D sont la piste de sécurité double, environ 20 dB plus silencieuse. Utilisez les fichiers principaux ; gardez _D pour rapiécer les zones écrêtées.
2. Assembler les fichiers de l'enregistreur
bash <skill>/scripts/stitch.sh "<folder>/audio" "<SYNC_OUT>/lav-master.wav"
Il lit time_reference (échantillons depuis minuit) de chaque fichier BWF et vérifie que chaque fichier commence exactement là où le précédent s'est arrêté. Lorsqu'ils sont contigus en échantillons, c'est une copie de flux sans perte. S'il y a un trou, il insère autant de silence, de sorte que le master reste à l'horloge temps réel, et prévient avec la taille.
3. Transcrire les deux côtés
bash <skill>/scripts/transcribe_sources.sh "<folder>" "<SYNC_OUT>/lav-master.wav" "<work>"
Tout atterrit dans <work>/sync/, gardé en dehors de <work> lui-même pour que sentences.py de paper-edit ne le lise jamais. L'audio de la caméra est d'abord fortement amplifié (highpass=f=120,volume=40dB,alimiter, 16k mono), car il est souvent autour de -65 dB. Ignore ce qui est déjà fait, donc relancez librement.
Le lapel prend environ 8 minutes par heure d'audio. Lancez-le en arrière-plan. Whisper diffuse les timings des mots dans <work>/sync/lav.log, et align.py lit ce journal quand lav.json n'est pas encore écrit, donc l'alignement peut commencer avant que le lapel ne soit terminé. Les clips au-delà du point transcrit signalent simplement "seulement N correspondances de mots" ; relancez une fois terminé.
4. Aligner
python3 <skill>/scripts/align.py "<work>"
Écrit <work>/sync/sync.json. La première exécution crée ~/.cache/lapel-sync-venv avec numpy et scipy (le python3 système n'a souvent ni l'un ni l'autre) ; après cela, il se réexécute simplement à l'intérieur.
Comment ça marche, pour que vous puissiez faire confiance ou remettre en question la sortie :
- Mots d'abord. Des suites uniques de 4 mots qui apparaissent dans les deux transcriptions. Décalage = temps lapel moins temps caméra. Des centaines de correspondances par clip ; groupe le plus dense, garder dans 0,5 s, médiane. Bon à environ 0,1 s.
- Affinement par forme d'onde. Chaque bloc de 20 s d'audio caméra (blocs de 6 s sur les clips de moins de 40 s), filtré passe-bande 300-3000 Hz à 8 kHz, est corrélé croisé avec le lapel dans +-0,6 s du décalage de mots. Les pics au-dessus de 6x la médiane comptent. Les blocs à moins de 20 ms les uns des autres sont d'accord ; sur les clips de plus de 5 minutes, un ajustement linéaire donne le décalage plus la dérive.
Lisez chaque ligne qu'il imprime : décalage, blocs d'accord, étalement, dérive. Tout clip marqué CHECK BY EAR (moins de 3 blocs d'accord) peut être décalé jusqu'à 0,1 s ; vérifiez-le dans Resolve avant de lui faire confiance. La dérive sur une frame à travers un clip est également signalée.
5. Découper le lapel par clip
python3 <skill>/scripts/slice.py "<work>" "<SYNC_OUT>/lav-master.wav" "<SYNC_OUT>"
Pour chaque clip, il écrit :
<SYNC_OUT>/<clip>_lav.wav: 24 bits, fréquence source, échantillon exact à la durée du clip, donc le temps lapel est égal au temps clip<work>/<clip>.wav(16k mono) et<work>/<clip>.json(whisper de la tranche) : ce que lisent paper-edit et video-reels<work>/clips.ndjsonavec un champ"audio"pointant vers la tranche
Si l'enregistreur a commencé tard ou s'est arrêté tôt pour un clip, cette partie est remplie de silence et il prévient. Whisper sur les tranches prend quelques minutes ; n'exécutez pas non plus transcribe.sh de paper-edit après (il sauterait de toute façon, puisque les json existent).
6. Vérifier l'écrêtage
slice.py compte les échantillons à ou au-dessus de 0,99 pleine échelle par tranche et imprime la plus longue série. Des centaines de courtes séries (dizaines d'échantillons) sont généralement le limiteur de l'enregistreur qui attrape les pics, pas de la distorsion. Seulement lorsqu'il répertorie des endroits à écouter (séries de 1 ms ou plus), ou que l'utilisateur dit que ça sonne crunchy, rapiécez ces moments à partir du jumeau _D (même timing, remontez-le d'environ 20 dB).
7. Construire dans Resolve
bash .claude/skills/paper-edit/scripts/ensure_resolve.sh
Écrivez <work>/sync/params.json :
{"project":"<Video> - YYYY-MM-DD","work":"<work>","name":"Synced - Full Talk",
"lut":"<LUT_PATH>"}
lut est optionnel ; supprimez-le si vous n'en utilisez pas. Puis un appel execute_resolve_code :
PARAMS = "<work>/sync/params.json"
exec(open("<skill>/scripts/resolve_sync_timeline.py").read())
Il crée le projet (ou l'ouvre si le nom existe), importe les clips caméra et les tranches lapel, et construit Synced - Full Talk : chaque clip sur V1 comme image uniquement (mediaType: 1), sa tranche sur A1 comme son uniquement (mediaType: 2), mêmes trames d'entrée et de sortie. recordFrame = début de la timeline + arrondi((décalage - décalage le plus ancien) * 24000/1001), de sorte que les vrais écarts entre les clips restent. LUT sur le nœud 1. L'audio brut de la caméra est laissé de côté. La timeline est en 1080x1920 lorsque les clips sont verticaux, 1920x1080 sinon (remplacez avec width/height). Si le nom de la timeline est pris, il ajoute v2 plutôt que de toucher à l'existante.
Demandez à l'utilisateur de scruter quelques endroits avec des consonnes claires (un "p" ou "t") sur chaque clip. C'est la vraie vérification.
8. Transmettre
Dites à l'utilisateur que la timeline existe, les nombres par clip de l'étape 4 en une ligne chacun, tout ce qui a été signalé, et le verdict d'écrêtage. Puis passez à autre chose : paper-edit (sautez son étape de transcription ; <work> est déjà configuré) ou video-reels.
Faits utiles
- La corrélation de forme d'onde seule a échoué. La corrélation sur tout le clip et par blocs de 20 s sur une large fenêtre a donné des résultats incohérents lorsque le micro de la caméra était à environ -65 dB moyen dans une pièce réverbérante. Les mots d'abord, puis une recherche de forme d'onde étroite, c'est ce qui fonctionne.
- L'horloge de la caméra est souvent erronée. Lors des tests, la caméra avait environ 149 s de retard sur l'enregistreur. Certaines caméras (ex. Sony) écrivent
creation_timeen UTC (se termine parZ), tandis quetime_referenceBWF d'un enregistreur est local. Traitez creation_time comme un indice approximatif au mieux. La correspondance de mots n'en a pas besoin. - La dérive est négligeable sur le matériel testé : environ 0,5 ms/min, 11 ms sur un clip de 21 minutes. Pas de ré-étirement temporel nécessaire. Les clips courts signalent 0 car un ajustement sur quelques secondes n'est que du bruit.
- La réexécution des scripts a reproduit des tranches construites manuellement à moins de 6 ms.
- Séquence verticale : par ex. un Sony filmant en vertical enregistre en 4K 23,976 avec une matrice d'affichage rotation=90. Resolve le lit en 2160x3840 sans correction de rotation nécessaire.
- Exemple d'enregistreur, Tascam DR-10L : 48k 24 bits mono BWF, nouveau fichier toutes les 15 minutes, jumeau de sécurité
_Denviron 20 dB plus bas. resolve_build.pyde paper-edit prend toujours le son du fichier caméra. Sur un tournage lapel, ses timelines portent l'audio brut de la caméra jusqu'à ce qu'il apprenne à lire le champ"audio". En attendant, dites-le à l'utilisateur, ou reliez A1 aux tranches_lav.wav; les points de coupe sont les mêmes car le temps de tranche est égal au temps clip.- Whisper s'exécute avec
-oj -ml 1 -sowpour les timings des mots.
Associé
paper-edit: le montage brut, exécuté après celui-civideo-reels: litclips.ndjson; le champ"audio"lui dit de prendre le son de la tranche (si installé)