Verification¶
Everything was checked with DaVinci Resolve Studio 21.1.1 on macOS. Other versions, and the free version, are untested. The method is always the same: change one thing (in the UI, with the scripting API, or by writing a stored value with drpkit write), export, compare — and where sound is involved, render and measure.
Reader¶
| What | How |
|---|---|
| factory defaults | a new project's mixer, compared with the defaults shown in the UI and the user manual (tests/expect_fixture_default-01.yaml) |
| built-in Dynamics | 309 values read on the UI: a write sweep of 23 projects × 10 knobs plus individual points (Conversions); a blind check on 20 points not used for the tables |
| writer round trip | writer → import into Resolve → export: every written value is kept (tests/expect_fixture_roundtrip-01.yaml) |
| fader | track −60 / −200 and bus −60 / −200 / +60 rendered −6.0 / −20.0 / +6.0 dB; bus fader −20 dB renders exactly 20.0 dB quieter; bus mute renders silence |
| EQ | type, frequency, gain, Q (6 points), range, slope set in the UI one control at a time |
| processing order | all six orders selected in the UI |
| plugin knobs | two or more UI points per linear knob plus the knob scale seen in the UI |
| track Voice Isolation on/off | toggled in the UI on two tracks and with Timeline.SetVoiceIsolationState |
| clip volume and fades | SetFades 0/0, 48/24, 100/7 and an FCPXML clip volume of −6 dB; kept after re-import |
| fade curves | rendered at curve points 25 / 50 / 75 / 78 %, plus 7 curves made in the UI |
| volume keyframes | keys made in the UI at +20 s / +25 s / +30 s 12 f; a clip with In = 144 to fix the frame origin; playback interpolation measured by rendering a synthetic tone |
| clip built-in FX | set and removed with SetVoiceIsolationState and SetProperty |
| markers | 16 colors, durations 1–24, Japanese text, XML characters and customData made with Timeline.AddMarker; re-encoding the four exports reproduces them byte for byte |
| timeline start | 3600 s → GetStartFrame() 86400 and 0 → 0 at 24 fps after import |
Compose¶
| What | How |
|---|---|
| media formats | 56 composed projects covering all 28 media format combinations (WAV 16/24/32f, FLAC 16/24, AAC, MP3 × 44.1/48 kHz × mono/stereo) imported with every clip Online at the requested positions, In points and fades, audible on both channels |
| mixer preserved | a 4-speaker template with plugins and Dynamics, composed with cuts, a crossfade, a fade-out and keyframes, kept its mixer and matched the original's loudness section by section (0.0 LU) |
| several files on one track | mono FLAC and stereo WAV on a mono track imported Online with one media-pool item each |
| markers | written into a template without markers, replacing existing markers, and with absolute frames on a timeline whose first cut is not at the template start — all read back identically by Timeline.GetMarkers() |
Automated tests¶
GitHub Actions runs them on Ubuntu and macOS with Python 3.9 and 3.12, plus a privacy scan of every committed .drp. The compose tests need ffmpeg / ffprobe.
| Test | What it checks |
|---|---|
test_dyn_law |
Dynamics conversion vs. values read on the Resolve UI |
test_fixtures |
reader defaults and round trip, fades, bus fader, writer (only requested cells change, refuses to overwrite, no output and no temporary file left on error, --timeline with several timelines) |
test_drp_compose |
composition: reader round trip, mixer and archive preserved byte for byte, media metadata for 28 formats against Resolve's own exports, invalid cut lists rejected without output, written file read back and checked against the cut table (a tampered Duration / In / media path / crossfade is refused), no file left on a failed write, crossfade handles, keyframes, clip FX inheritance |
test_markers_media_cli |
track Voice Isolation on/off, markers (read, byte-exact re-encode, compose add / replace / keep / clear, invalid input), several media files per track, the drpkit command line, and the privacy scan itself |
The docs build checks that the generated tables match drpkit/fairlight_map.yaml (python tools/gen_map_docs.py --check).
Fixtures¶
Fixtures are synthetic: new Resolve projects with generated sine tones. No real projects or recordings are included, and fixtures carry only .drp exports — imported into Resolve their media shows as offline (the tests do not need it).
Before a .drp is committed it goes through tools/sanitize_drp.py, which neutralizes the machine id, the Resolve user UUID (replaced by a fixed public one) and the absolute paths Resolve stores in the project settings; tools/sanitize_drp.py --check must report nothing. The check looks for home / volume paths (macOS, Linux, Windows), any e-mail address, machine ids and user UUIDs, also inside hex blobs and zlib / zstd streams, unpacked recursively.
To relink the first fixtures in Resolve, generate the two files (10 s, 48 kHz, mono, 16-bit) at the paths the project expects:
mkdir -p /tmp/rfr-fixture
ffmpeg -f lavfi -i "sine=frequency=1000:sample_rate=48000:duration=10" -ac 1 -c:a pcm_s16le /tmp/rfr-fixture/sine1k.wav
ffmpeg -f lavfi -i "anullsrc=r=48000:cl=mono" -t 10 -c:a pcm_s16le /tmp/rfr-fixture/silence.wav
Limits of the evidence¶
- Calibration tables (※) checked against the same Resolve version they were built from are circular; the independent evidence is the blind check and renders.
- A byte-identical mixer is not the same as identical sound in every case; the loudness comparison covers one template.
- Everything may change in any Resolve release.