.prproj), разные инструменты и разный смысл кода возврата. Смешивать их нельзя по одной причине: прибор, которым проверяешь, обязан быть точнее того, что проверяешь. Родной Clip → Synchronize в Premiere кладёт клипы только на целые кадры — 40 мс при 25p, — а численный синхрон держит 1–14 мс. Поэтому Premiere здесь не корректор, а независимый свидетель: он может подтвердить, но не уточнить.Ингест отвечает на вопрос «где стоит каждый клип» и заканчивается числом в ingest.json. Проверка отвечает на другой вопрос — «этому числу можно верить?» — и заканчивается вердиктом. Разделить их пришлось после дня, когда обе роли были в одной кнопке: Collect и мерил, и тут же переписывал ингест. Пока он был один, любое измерение немедленно становилось правкой, и грубый прибор молча портил точный.
ingest.json + секвенции в Premiereingest.json + сохранённый .prproj⚠️ Практическое следствие: у проверки нет права записи по умолчанию. Она сначала печатает таблицу, а пишет только по явному подтверждению человека.
| прибор | что меряет | метод | разрешение | независим от |
|---|---|---|---|---|
Verify Syncverify_sync.py | каждую пару звуковых дорожек собранной сцены — по факту .prproj, не по плану | корреляция огибающих речи 200–3800 Гц, шаг 4 мс (функции timeline_sync_audit.py) | единицы мс, вердикт в кадрах | планировщика и ингеста |
finesync | поправки ко всем источникам сразу | корреляция огибающих речи + МНК | 1–3 мс | — |
pair_check | каждую пару источников отдельно | GCC-PHAT на 48 кГц | 0,008 мс | finesync (другой метод) |
Clip → Synchronize | куда Premiere сама подвинет клип | внутренний, закрытый | кадр = 40 мс | всего нашего кода |
prproj_verify | совпал ли собранный таймлайн с планом | чтение .prproj | 0,5 кадра | панели UXP |
Ценность родного Synchronize — не в точности, а в независимости: это чужой алгоритм, написанный другими людьми и не знающий о наших числах. Если он согласен — совпадение неслучайно. Если нет — есть повод перемерить точным прибором, а не переписать точное грубым.
⚠️ pair_check намеренно сделан другим методом, чем finesync: фаза (GCC-PHAT) против огибающей громкости. Два прибора с одним методом согласятся и на общей ошибке.
Главное правило этапа, и оно нарушается легче всего. Шкала, в масштабе:
0,008 мс pair_check (GCC-PHAT) ▏ 1–3 мс finesync ▍ 13–14 мс худшая пара на реальном дне ██ 20 мс половина кадра ███ 40 мс КАДР — пол Premiere ██████
Всё, что Synchronize способен сказать, — это «ноль, один кадр или два». Дробей у него нет: и наша раскладка, и его ответ лежат на одной кадровой сетке. Поэтому «дельта 40 мс» после него значит не «рассинхрон 40 мс», а «округление в одну сторону против нашего округления в другую».
⚠️ Записать его ответ в ингест — значит заменить измерение в 13 мс округлением в 40. Петля Spread → Synchronize → Collect квантована с обеих сторон, и на хорошем ингесте она может только ухудшить.
⚠️ И обратное: «все пары в допуске» от грубого прибора — не аттестат. Он не умеет померить то, что проверяет, и его «PASS» — это отсутствие мнения, а не одобрение.
Premiere включает пункт меню не всегда, и причины нигде не документированы. Проверено на живом проекте — условий два:
В выделении не больше одного клипа на каждой дорожке. Два — и пункт гаснет.
Предположительно нужны включённые цели дорожек. На практике не подтвердилось: пара сработала при MZ.TrackTargeted=0 у второй дорожки.
⚠️ Источник правила G1 — 18-секундный ролик, у Adobe документации на него нет. Раскладка сделана так, чтобы условие выполнялось по построению, но выдавать его за документированное нельзя.
рабочий таймлайн урока стенд _SYNC (одноразовый)
V1 FX3 ████ ███ ██████ V1 FX3-1263 ████
V2 ZVE1 ████ ███ ██████ V2 ZVE1-1946 ████
V3 экран ██████████████ V3 ZVE1-1947 ███
V4 ролики ██ █ V4 FX3-1264 ███
A5 петличка ██████████████ ... по клипу на дорожку
A6 петличка ██████████████ A11 петличка ██████████████
A12 петличка ██████████████
Synchronize СЕРЫЙ по праву: Synchronize активен:
на V1 пять клипов подряд на каждой дорожке ровно один
.sqpreset, а не засевается клипом. В UXP нет API создания дорожек, и панель рождала их вставкой одноразовой заглушки — а удаление в живой Premiere отрабатывает вхолостую. Пресет создаёт нужное число дорожек сам: ни сида, ни заглушек, ни мусора.mTargeted пишется на каждую аудиодорожку, и это единственный способ отдать секвенцию человеку с уже включёнными целями: API таргетинга в UXP нет. ⚠️ Блок <VideoTracks> в пресете пуст — видеодорожки подорожечно не настраиваются.videoTrackIndex = -1) нельзя: у A/V-клипа Premiere всё равно кладёт видео, и кладёт всё на V1. Параметр работает только на настоящих аудиофайлах — петличка ложится так честно.Sequence.setSelection, а не человек через ⌘A: ⌘A ловит однокадровый мусор, и на дорожке оказывается два выделенных айтема.Только опоры синхрона — съёмочные камеры и петлички. Запись экрана и ролики-примеры исключаются, и не ради экономии дорожек:
⚠️ Раз часть источников исключена, Collect обязан вычитать общий сдвиг группы: Synchronize равняет всё по одному опорному клипу, и без вычитания исключённые источники уезжают относительно включённых на всю его величину при каждом прогоне.
Audio track channel на все клипы разом. Замер: у одной камеры правый канал −91 dB, цифровая тишина. Взять его — и эта камера останется без опоры, Premiere оставит её на месте, а это читается как «дельта 0», неотличимо от идеального совпадения. Петличка вдобавок моно. Канал 1..prproj), кладёт заказ в /tmp/ytai_verify_sync.json и открывает Терминал: звук внутри UXP мерить нечем, нет ffmpeg.verify_sync.py снимает дамп секвенции (00_Setup/logs/verify_sync_dump.json) и меряет каждую пару звуковых дорожек функциями арбитра timeline_sync_audit.py — на частоте самой секвенции, поиск ±6 с.00_Setup/01_Ingest/{CODE}_sync_verdict.json.| вердикт | что значит | что делать |
|---|---|---|
| SYNC ✓ | худшая пара камера ↔ петличка ≤ ½ кадра | сцена годна, дальше цвет |
| DESYNC ✗ | есть пары камера ↔ петличка больше ½ кадра; статус называет худшую и дорожки | точный прибор по этой сцене (pair_check), затем чинить числом — не двигать клипы |
| INSTRUMENT ✗ | контроль петличка ↔ петличка не ≈ 0 — сломан замер, а не проект | остальным числам не верить; разбирать прибор |
| NO DATA | ни одной пары камера ↔ петличка с достаточным перекрытием и ясным пиком (пик < 0,25 не судится) | проверить, есть ли петличка на сцене; судить приборами ниже |
Пары камера ↔ камера — справочно, в гейт не входят. Тот же замер из терминала: python3 verify_sync.py --dump dump.json --fps 25 --seq {CODE}_{сцена} --json-out verdict.json (0500_uxp/tools/verify_sync/). Промах в минуты кнопка не ищет — для него timeline_sync_audit.py --dump … --max-shift 40.
V=~/YTAI/environment/.venv_transcribe/bin/python W=~/YTAI/scripts/999_extra/wordsync_multicam $V $W/pair_check.py --project "$P" # по каждой паре, GCC-PHAT $V $W/pair_check.py --project "$P" --scene 02_... # одна сцена, 8 окон python3 $W/prproj_verify.py --project "$P" # собранное против плана
⚠️ pair_check требует пересечения по времени не меньше 2 × win-sec (по умолчанию 30 с). Пара короче молча не судится — её просто нет в отчёте.
python3 $W/sync_ready.py --project "$P" # все секвенции, ⌘A python3 $W/sync_ready.py --project "$P" --selection pairs # модель «выделил пару» python3 $W/sync_ready.py --project "$P" --strays # что снести руками python3 $W/sync_ready.py --project "$P" --also-autosave # сохранённое vs автосейв
Печатает по каждой дорожке: клипов, огрызков, цель, кадр источника; матрицу перекрытий; и вердикт тремя блоками, которые нельзя смешивать — жёстко (проверено) · по опыту (у Adobe не документировано) · не читается из .prproj. Замок дорожки в проекте не хранится вообще, и прибор честно говорит это, а не выдумывает колонку.
⚠️ --also-autosave сверяет сохранённый проект со свежайшим автосейвом. Классическая потеря вечера: секвенция живёт в автосейве, а в сохранённом файле её нет, потому что ⌘S нажали до того, как раскладка дошла до конца.
Clip серое целиком) → Clip → Synchronize → Audio track channel 1 → OK._SYNC, сгенерированный .sqpreset и импортированные стендом петлички. Манифест sync_session_*.json оставить — это архив того, что проверяли.⚠️ Кнопка Clean выделяет весь однокадровый мусор; если снос не сработает (а он часто не срабатывает) — нажать ⌫. Это же способ вычистить огрызки из уже собранных секвенций уроков.
Худший остаток по камерам меньше кадра. Значит чужой алгоритм не нашёл расхождения в пределах своего разрешения. Писать нечего — и это правильный исход этапа, а не отказ.
Повод перемерить точным прибором — pair_check по этой сцене, — а не переписать ингест. Грубый прибор показал, где смотреть; что именно там, скажет точный.
⚠️ Отдельный ложный исход: «ни один источник не сдвинулся». Клип, который Premiere не сумела сопоставить, она оставляет на месте, и это читается как «дельта 0» — неотличимо от идеального совпадения. Поэтому сторожить надо сырые дельты, а не остатки: при идеальном синхроне остатки и так в нуле, и проверка «ничего не двинулось» на них ложно срабатывала бы именно на лучшем случае.
⚠️ Петличкам поправки не пишутся никогда. После frame-align это не записи рекордера, а timeline-domain рендеры: поправка уже запечена в сам звук, wall_offset у них ноль. Сдвинуть им число — значит обесценить рендер, звук в файле останется на месте. Остаток по петличке больше кадра означает «перегони 4.4», а не «подвинь».
Три урока, две камеры, две петлички, 25p.
| что | число | чем измерено |
|---|---|---|
| пар в допуске | 37 из 37 | pair_check, GCC-PHAT |
| худшая пара | 14,2 мс = 0,36 кадра | то же |
| дельты Premiere | 0, 40 или 80 мс — все кратны кадру | Clip → Synchronize |
| собранное против плана | 17/17, 13/13, 5/5 | prproj_verify |
| артефактов вставки | по 4 на сцену | то же |
Систематика, которую видно только точным прибором: одна камера держится одной петлички, другая — другой, между группами ~11 мс при разбросе в отдельных парах до 0,5 мс. Семь независимых замеров в коридоре +9,9…+11,7. Это не рассинхрон, а разница акустического пути или задержка звука в кодеке камеры — и «исправление» увело бы камеру от её же звука.
⚠️ 11 мс — 0,27 кадра. Значит это решает исход только там, где клип и так стоит у границы кадра. На одной из сцен шесть клипов из восьми стояли в 1,3–6 мс от половины кадра — именно там Premiere и разошлась с нами на два кадра, а на остальных, где запас 13–18 мс, на один.
Приборы выше проверяют камеры и петличку. Запись экрана они не трогают, и до 25.09.2026 её позицию не мерил никто ни разу. Ингест кладёт её так: sync_source: "lesson_start", wall_offset: 0, fine_sync: {} — встык от начала урока. Второй части достаётся wall_offset, равный длительности первой, то есть снова встык. Проверено на всех одиннадцати версиях ингеста YTUVIE01, лежащих на диске.
⚠️ creation_time записи экрана в ингесте — подделка. У YTUVIE01 он стоял в 100 мс от creation_time первой камеры урока: поле дописано под lesson_start, а не прочитано из файла. Настоящее время лежит рядом в device_creation_time и с ним не совпадает. По creation_time синхрон записи экрана проверить нельзя — он выглядит связно и врёт.
4.2 Fine Sync эту дыру не закрывает: он берёт окна внутри предполагаемого пересечения и доводит уже примерно верную раскладку. Промах в минуты для него вне поля зрения.
WAV петлички в координатах таймлайна — рендер 0113 TX01_{сцена}_timeline.wav в Sound/ урока (полевой день — 01_Source/{сцена}/Sound/). Не путать с лежащим рядом срезом урока {CODE}_{урок}_TX01_timeline.wav: он в стенном времени урока, паузы в нём не вырезаны. Полная длина секвенции, старт = таймлайн 0. Значит позиция записи экрана вычитывается из него напрямую, без промежуточной математики.
Полная взаимная корреляция огибающих речи, FFT. Полоса 200–3800 Гц, шаг 4 мс — те же, что у finesync, чтобы число было сопоставимо с тем, чем меряли камеры.
| Запись экрана YTUVIE01 | стояла | замер | промах | пик |
|---|---|---|---|---|
1.1.1. Первая часть.mov | 0:00 | 2:06.660 | +2:06.66 | 0.98 |
1.1.1.1. Вторая часть.mov | 18:26.64 | 24:23.710 | +5:57.07 | 0.97 |
1.1.2..mov | 0:00 | 0:33.780 | +0:33.78 | 0.99 |
1.1.3.mov | 0:00 | 0:06.276 | +0:06.28 | 0.99 |
Вторая петличка подтвердила три замера из четырёх в пределах 6 мс. У первой части TX02 дал ложный пик 0.35 против 0.98 у TX01 — там вторая петличка молчала; судить по слабому пику нельзя.
⚠️ Две грабли самого замера, обе поймались на живых данных.
Свес за конец опоры. Искать только позиции, где цель влезает в опору целиком, нельзя: запись экрана обычно идёт до конца урока и торчит за конец петлички на секунды. Тогда верный ответ оказывается за пределом поиска, а функция упирается в край и возвращает мусор — на уроке 1.1.3 ответ 6.28 с при пределе поиска 4.3 с и пик 0.161. Опору дополнять нулями, нормировать по реально перекрывшемуся куску.
Дрейф проверять четвертями. Гонять файл по четвертям отдельно: если все дают одно и то же начало файла ±1 мс — дрейфа нет и число верное. Низкий общий пик при согласных четвертях означает лишь размытый хвост, а не плохой замер (вторая часть: целиком 0.67, по четвертям 0.93–0.99).
Замеренное пишется в wall_offset ингеста, sync_source становится speech_xcorr, рядом кладётся бэкап — как это делает finesync. Дальше Build Ingest: поправки в готовый таймлайн сами не приедут.
⚠️ Исправлено 25.09.2026 вечером. В первой редакции этой карточки причиной был назван ручной сдвиг клипов на таймлайне. Это неверно: клипы никто не двигал. Паузы убирает сам билдер, и дефект в нём. Физика ниже описана верно, виновник был назван неправильно — исправление оставлено на виду, потому что ошибка ровно того сорта, о котором эта глава.
После 4.3 Frame-Align петличка на A5/A6 — это один отрендеренный WAV во всю длину секвенции, а не нарезка. Его ось времени — настенная: содержимое в точке t соответствует настенному t.
Билдер при укладке выбрасывает паузы между дублями: клип встаёт не на свой wall_offset, а вплотную к предыдущему. Куски петлички он при этом режет по тем же границам — то есть режет верно, — но внутри куска ставит source_in == timeline_start. Смещение, накопленное выброшенными паузами, в source_in не попадает. Видео уехало, звук остался.
| YTUVIE01, урок 1.1.1 | расхождение | что это значит |
|---|---|---|
| камеры ↔ камеры | −8…+62 мс | между собой целы |
| клипы ДО первой выброшенной паузы | −375, −437 мс | накопленного сдвига ещё нет |
| клипы ПОСЛЕ неё ↔ петличка | −28,7 с | ровно выброшенная пауза |
| TX01 ↔ TX02 | −6 мс | контрольная пара: прибор исправен |
В уроках с паузами поменьше промах вышел −177…−322 мс — так же незаметно на слух и так же неверно.
⚠️ Почему это не ловилось годами. prproj_verify.py сверяет собранный таймлайн с планом, который считает тот же самый планировщик, — и честно пишет «сошлось 21 из 21, расхождений нет». Проверялось «легло ли по плану», а не «верен ли план». Сверка с собственным планом — не проверка. Единственный внешний арбитр здесь — сам звук.
⚠️ Ингест тоже не истина. В том же проекте у двух клипов последнего дубля (RYA-ZVE1-1954, RYA-FX3-1271) сам wall_offset промахивался на −1,88 с, и это выглядело как «пауза 1,96 с». Остальные 18 клипов сошлись с ингестом в пределах ±41 мс. Поправку поэтому берут из замера звука, а не из ингеста: починка «по ингесту» уводит ровно на его собственную ошибку.
Чинится без Premiere. Петличка — рендер, обычный файл. Новый WAV строится так, что identity-укладка становится верной: new[t] = old[t + сдвиг(дубль)]. Сдвиг берётся замером — где содержимое клипа реально лежит в петличке минус где клип стоит на таймлайне; сегментом служит дубль, а не урок (на уроке 2 усреднение по уроку оставляло 80 мс, по дублям — 26 мс). Результат на YTUVIE01: 28 730 → 31 мс, 322 → 26 мс, 248 → 22 мс, все три урока внутри кадра.
⚠️ Формат петлички — pcm_f32le. Пересобрать её в pcm_s24le значит срезать всё, что выше 0 dBFS: во float это законный сигнал, и у двух файлов пик упирается ровно в ноль. Кодек и раскладку каналов брать с источника, оригиналы складывать рядом.
| файл | что делает |
|---|---|
0500_uxp/tools/verify_sync/verify_sync.py · .command | кнопка Verify Sync: дамп сохранённого .prproj → вердикт по сцене в кадрах |
999_extra/wordsync_multicam/timeline_sync_audit.py | арбитр: попарная сверка звука по факту таймлайна (его функции зовёт кнопка) |
999_extra/wordsync_multicam/screencast_offset.py · relav2.py | позиция файла в петличке полной корреляцией · пересборка петлички по замеру |
999_extra/wordsync_multicam/pair_check.py | GCC-PHAT по каждой паре, без Premiere |
999_extra/wordsync_multicam/sync_ready.py | предполётная: будет ли команда доступна |
999_extra/wordsync_multicam/sync_verdict.py | вердикт после синхрона, из .prproj |
999_extra/wordsync_multicam/prproj_verify.py | собранный таймлайн против плана |
999_extra/wordsync_multicam/spread_fcpxml.py | раскладка файлом, в обход панели |
999_extra/prproj_timeline_extract.py | общий читатель .prproj для приборов из проекта |
0500_uxp/src/ingest/syncSpread.js | раскладка, контракт L1–L9, отбор выделения, вычитание базы |
0500_uxp/src/ingest/placement/sequencePreset.js | генератор .sqpreset с mTargeted |
0500_uxp/src/ingest/placement/syncSelection.js | чтение таймлайна и setSelection |
Соседи по флоу: 4.1 Word-Sync ставит день на общую ось, 4.2 Fine Sync доводит до миллисекунд, 4.3 Frame-Align кладёт на кадровую сетку. Эта страница — про то, как убедиться, что всё три сработали.
Synchronize ценен независимостью, а не точностью: его пол — кадр, наш синхрон — миллисекунды. Поэтому он подтверждает, но не уточняет, а Collect сначала отчитывается и только потом, по явному подтверждению, пишет.