Skip to content

jerich/cheepsync

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 

Repository files navigation

cheepsync

Multi-camera video synchronization using inaudible acoustic markers — no timecode hardware, no clapper board, no HDMI sync cable. You press a button; every camera in the room hears a short ultrasonic tone burst; post-processing aligns the recordings by matching the tone.

How it works

cheepsync-mark emits a ~200ms FSK-encoded burst at 18 kHz (adjustable) when you press a button. Each press broadcasts a unique marker ID (0–255 per session) and logs the exact wall-clock time. Think of it as a digital slate clap.

cheepsync-sync finds those markers in your recorded video files and either:

  • --find — prints a table of which markers appear in which files and when
  • default — prepends blank video to earlier-starting cameras and low-pass filters the sync tone out of all tracks, producing aligned files you can drop into any editor

Requirements

  • Python 3.9+
  • ffmpeg and ffprobe on PATH (for cheepsync-sync)
  • A speaker within ~3 m of all cameras (Pimoroni Pirate Audio 3W hat works well on the Pi)
pip install cheepsync           # mac/linux
pip install cheepsync[rpi]      # Raspberry Pi (adds gpiozero)

Workflow

1.  Start recording on all cameras.
2.  Press the cheepsync-mark button (or spacebar) — one press per sync point.
3.  Stop cameras.
4.  Run cheepsync-sync to align the files.

Pressing the button multiple times creates redundant sync points — useful if one recording was too noisy or the signal was blocked. You only need one common marker between cameras.


cheepsync-mark

Plays a marker burst on each button press and writes a session log.

cheepsync-mark [OPTIONS]
Option Default Description
--base-freq 18000 Carrier frequency in Hz. Reduce if cameras can't pick up 18 kHz.
--fsk-shift 500 Frequency shift for FSK bit-1 (Hz).
--sample-rate 48000 Audio sample rate.
--device system default sounddevice output device index or name substring.
--list-devices Print available output devices and exit.

Trigger:

  • Raspberry Pi — physical button on BCM 5 (Pimoroni Pirate Audio A button, detected automatically via gpiozero)
  • Mac / Linux — press SPACEBAR; Q or Ctrl+C to end session

Session log — written to the current directory as cheepsync_YYYYMMDD_HHMMSS.json:

{
  "session_id": "20260406_142315",
  "started": "2026-04-06T14:23:15.000Z",
  "base_freq": 18000.0,
  "fsk_shift": 500.0,
  "markers": [
    {"id": 0, "time": "2026-04-06T14:23:45.123Z"},
    {"id": 1, "time": "2026-04-06T14:24:12.456Z"}
  ]
}

cheepsync-sync

Detects markers in video files and produces aligned output.

cheepsync-sync [OPTIONS] VIDEO [VIDEO ...]

Find mode (no re-encoding)

cheepsync-sync cam_a.MOV cam_b.MOV --find

Prints a table of every detected marker, which files contain it, and the time it appears in each:

  Marker  cam_a.MOV     cam_b.MOV
  ------  ----------    ----------
       0 ✓  10.000s      6.300s
       1 ✓  25.000s     21.300s
       3    89.001s         —

  2 of 3 marker(s) present in all files.

Pass --session-log cheepsync_*.json to annotate the table with wall-clock times.

Sync mode (default)

cheepsync-sync cam_a.MOV cam_b.MOV --output-dir ./synced
  1. Detects markers in each file
  2. Picks the common markers and computes per-camera time offsets
  3. Generates a _START offset clip for each non-reference camera
  4. Place each _START clip before its video on the NLE timeline to align

Optionally pass --lowpass-hz 17500 to also generate _filtered copies with the sync tones removed from audio.

Options

Option Default Description
--base-freq 18000 Must match what cheepsync-mark used.
--fsk-shift 500 Must match what cheepsync-mark used.
--sample-rate 48000 Audio analysis sample rate.
--session-log Optional .json from cheepsync-mark for wall-clock annotation.
--find Report only; no output files.
--output-dir . Where to write offset clips and filtered files.
--lowpass-hz 0 Low-pass cutoff for filtered copies (0 = disabled). Set to 17500 to remove sync tones.
--threshold 10.0 Goertzel magnitude threshold factor (noise_floor × factor). Raise if too many false positives; lower if markers are missed.
--debug Print detection details for each candidate onset (verbose).
--verbose Print ffmpeg progress and metadata.

Signal structure

Each marker burst is 200 ms total:

[30ms sync pulse @ base_freq] [10ms silence] [8 FSK bits × 20ms each]

The 30ms sync pulse followed by 10ms of silence acts as a structural header: the FSK decoder skips past it to find the 8 data bits that follow.

Detection pipeline:

  1. Goertzel filter at base_freq in 5ms windows — computes magnitude at the exact sync tone frequency (single DFT bin, no broadband pre-filter needed)
  2. Threshold onset detection — Goertzel magnitude must exceed noise_floor × threshold_factor, with a 250ms refractory period between detections
  3. FSK decode — at each onset, skip the sync pulse + gap, then compare Goertzel magnitudes at the two FSK frequencies across 8 bit windows

Frequency selection

18 kHz is the default — above the hearing range of most adults, below the recording cutoff of common consumer cameras. Some cameras roll off above 15–16 kHz.

If markers aren't detected: open a recording in Audacity, switch to Spectrogram view (range 14–22 kHz), and look for a short bright stripe at 18 kHz at the moment of the button press. If no stripe is visible, lower --base-freq in 500 Hz steps until it appears.

If you hear the tone: lower --base-freq toward 18000–19000 Hz to move it higher. Young ears can sometimes hear above 17 kHz; test with your youngest participants.

If false positives persist: try a different --base-freq that doesn't coincide with camera electronics noise. Common interference bands: 15.6 kHz (CRT horizontal scan, rare now), 17–19 kHz (some LED drivers), 20 kHz (some camera DSPs).


Raspberry Pi setup (Pimoroni Pirate Audio 3W)

pip install cheepsync[rpi]
cheepsync-mark --device "sndrpihifiberry"   # find exact name with --list-devices

The MAX98357A I²S DAC on the Pirate Audio hat appears as an ALSA device. Use --list-devices to find its name on your system. Output is always stereo (L+R identical) as required by the I²S DAC.

The A button (BCM 5) is used automatically when gpiozero is installed. No configuration needed.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages