Multicam audio sync and director-style auto-edit — align N angles of one event by audio cross-correlation, then cut/PiP them into one MP4. Reversible sidecars, never re-encodes the originals.
Project description
polysync
Multicam audio sync + director-style auto-edit. Align N recordings of the same event by audio cross-correlation, then cut or picture-in-picture them into a single MP4 — driven entirely by who's talking.
What makes it different from "yet another sync tool":
- Reversible sidecars, never re-encodes the originals. Sync writes a tiny
<input>.sync.jsonnext to each file holding a single offset. A 75-min 4K 3-camera shoot is 250+ GB; baking offsets into re-encoded copies would double that and lose quality. Downstream applies the offset withffmpeg -itsoffsetat consume time. Originals are touched read-only, always. - Envelope cross-correlation, not raw waveform. Matches the log-energy envelope, which both mics hear regardless of their frequency response — robust even when a second camera's on-board mic sounds nothing like the main one.
- Clock-drift aware. Cheap recorders drift 5–50 ppm; polysync fits the drift across the recording and reports it separately, so long-form lip-sync can correct it while camera-cut editing can ignore it.
- Handles the messy real cases. Auto-picks the loudest audio track (pro cameras often leave track 1 dead), partial-coverage clips that only span part of the session, and independent verification of the result.
Install
pip install polysync # once published
# or, from a checkout:
pip install -e ".[dev]"
Requires Python ≥ 3.9 and ffmpeg / ffprobe on your PATH
(brew install ffmpeg, apt install ffmpeg, …). Python deps: numpy, scipy.
Quickstart
# 1. Sync each angle to a reference camera (writes <file>.sync.json sidecars)
polysync sync CAM_A.mp4 CAM_B.mxf
polysync sync CAM_A.mp4 CAM_C.mxf
# 2. (optional) Verify the alignment — re-checks residual independently
polysync verify CAM_A.mp4 CAM_B.mxf CAM_B.mxf.sync.json
# 3. Build an auto-edit decision list (who's on screen each second)
polysync edit CAM_A.mp4 CAM_B.mxf CAM_C.mxf --out edl.json
# 4. Render — hard cuts, or with a picture-in-picture inset
polysync render-cuts edl.json --out final.mp4
polysync render-pip edl.json --out final.mp4 --pip bottom-right
A clip that only covers part of the session (a Riverside / phone / lavalier recording that started mid-way):
polysync sync REFERENCE.mp4 PARTIAL.m4a --partial
How it consumes the sidecar
delta_seconds is the source's t=0 in the reference's timeline (positive =
source starts later). To align by hand:
ffmpeg -itsoffset $(jq -r .delta_seconds CAM_B.mxf.sync.json) -i CAM_B.mxf \
-i CAM_A.mp4 -filter_complex "[0:v][1:v]hstack" out.mp4
The edit / render-* commands read every sidecar automatically.
Python API
from polysync import compute_sync # pure-numpy core, unit-testable
from polysync.sync import sync_files # file → sidecar
from polysync.verify import verify_files
from polysync.edit import build_edl
Status
Beta (0.1). Sync + verify are battle-tested on real Sony FX3/FX6 multicam interview footage; the auto-edit is audio-energy-driven (no face detection). Issues and PRs welcome.
License
MIT © 王建硕 (Jian Shuo Wang)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file polysync-0.2.0.tar.gz.
File metadata
- Download URL: polysync-0.2.0.tar.gz
- Upload date:
- Size: 24.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffba8c0f75c2ce30efd11efdb68e2c8663411ba87ec9379d9ed8630f53225805
|
|
| MD5 |
8e4893f10af95b4706e61a4eb940febe
|
|
| BLAKE2b-256 |
7531fa5b9018289901c26b4fe8f44ddc46245eb6c2819d51bfa3eb881064716c
|
Provenance
The following attestation bundles were made for polysync-0.2.0.tar.gz:
Publisher:
publish.yml on jianshuo/polysync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polysync-0.2.0.tar.gz -
Subject digest:
ffba8c0f75c2ce30efd11efdb68e2c8663411ba87ec9379d9ed8630f53225805 - Sigstore transparency entry: 1630271071
- Sigstore integration time:
-
Permalink:
jianshuo/polysync@3e8d1bd9be6b2d939b9a85e7f0c015ec576c1d65 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/jianshuo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3e8d1bd9be6b2d939b9a85e7f0c015ec576c1d65 -
Trigger Event:
release
-
Statement type:
File details
Details for the file polysync-0.2.0-py3-none-any.whl.
File metadata
- Download URL: polysync-0.2.0-py3-none-any.whl
- Upload date:
- Size: 26.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af4c7bea79cfdf95f448958c66858756d2b6e59825c8e87a55da10d25c8cdd21
|
|
| MD5 |
26beed0cbf8ca5259fe8b4ebd96613ac
|
|
| BLAKE2b-256 |
b8b73b4b8eaf3f455fe2e0e6200a0000d6b8892e0fd22ed723846413e08a0048
|
Provenance
The following attestation bundles were made for polysync-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on jianshuo/polysync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polysync-0.2.0-py3-none-any.whl -
Subject digest:
af4c7bea79cfdf95f448958c66858756d2b6e59825c8e87a55da10d25c8cdd21 - Sigstore transparency entry: 1630271141
- Sigstore integration time:
-
Permalink:
jianshuo/polysync@3e8d1bd9be6b2d939b9a85e7f0c015ec576c1d65 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/jianshuo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3e8d1bd9be6b2d939b9a85e7f0c015ec576c1d65 -
Trigger Event:
release
-
Statement type: