Repository navigation
Releases
Audio Desync Finder 4.2.0
Audio Desync Finder 4.2.0
A fit that explains nothing is no longer presented as a model.
check_model_is_real() refuses to call it one when:
- the line is more than 1.5 s off the blocks,
- fewer than 20 % of the blocks agree with it,
- the offsets themselves span more than 20 s.
Instead of offset -11948.8 ms, drift +3.031 ms/s, atempo 1.003031 the panel now says what is true: these files do not look like the same film, or the intro differs so much that nothing lines up. The export button is disabled and export_plan() refuses, so nothing wrong can be written out.
The lag search follows the running time. When the files differ in length by more than the 90 s search window, every block searches the difference plus a minute — a very different intro can push the match minutes away from where the narrow search looks.
Measured on the pair from the screenshot
Nobody (2021) vs Nikt 2 (2025) -> "no model" (two different films)
Nobody 2 vs Nikt 2 (2025) -> 1 segment, -310 ms, RMS 3 ms, 66 of 67 blocks
Files
AudioDesyncFinder.pyw- 4.2.0
Audio Desync Finder 4.1.2
Audio Desync Finder 4.1.2
Proper dashes in titles and suggested names. pretty_title() turns - into –, and it is used for what the app shows and for the name it suggests when saving — never for a path it has to open, where the real characters matter.
- the window title now carries the film as well:
Audio Desync Finder 4.1.2 – <film>, - the export dialog suggests
<film> – corrected.mkanext to the target file instead of a barecorrected.mka.
Wielka mała koza - 2026 - wersja PL -> Wielka mała koza – 2026 – wersja PL
Files
AudioDesyncFinder.pyw- 4.1.2
Audio Desync Finder 4.1.1
Audio Desync Finder 4.1.1
The block scan is the default again.
The whole waveform is behind a Whole waveform switch now (off by default). It found the cuts well on one pair and wobbled on another, so it stays available without being in the way.
The Scanning ... in blocks of ... line is only printed when blocks are actually scanned, so the log no longer mentions blocks while the waveform is being followed.
Files
AudioDesyncFinder.pyw- 4.1.1
Audio Desync Finder 4.1.0
Audio Desync Finder 4.1.0
The whole waveform is followed, for every pair.
Instead of measuring a block every eighty seconds and fitting a model through those blocks, the offset is tracked second by second: each step takes six seconds of the reference, looks for it within ±1.5 s of the current offset, and holds the last value where it cannot find itself (re-recorded dialogue). The result is a curve; the segments are the runs of that curve.
- a median filter over fifteen seconds removes the tracker's twitch without moving a real step,
- runs shorter than twenty-five measured seconds are absorbed by the neighbour with the closer level, so a blink cannot open a segment,
- neighbours that end on the same level within 200 ms are joined,
- every boundary is then snapped by measurement: the fade zone counts only within 45 s (three minutes away it belongs to another cut), otherwise
locate_cut()searches ±90 s for the exact second.
Measured on the goat pair:
5974 seconds followed in about 7 s
five segments: +120 | -870 | -2050 | -3000 | -4305 ms
first cut at 10:03 (the block model said 11:20)
The block scan stays as the fallback, and Blocks only forces it.
Files
AudioDesyncFinder.pyw- 4.1.0
Audio Desync Finder 4.0.15
Audio Desync Finder 4.0.15
Why 10 / 10 loses the anomalies. A ten second window cannot tell a real lag from a coincidence when the search spans ±90 s: the offsets scatter over tens of seconds, the model collapses to one line whose RSS happens to be small, and the real cuts disappear into that cloud. A long window is what makes a lag unambiguous, and a short block is only useful for positioning once the offset is known.
The scan now says so instead of quietly returning a meaningless result:
A 10 s window is too short to tell a real lag from a coincidence over a ±90 s
search: the offsets scatter over tens of seconds, the model collapses to one line
and the real cuts disappear into the cloud. Use 40-80 s.
The table also stopped lying. A block with support 0.99 that sits away from the fitted line was labelled dialogue; it is now off line, and dialogue is kept for blocks that did not correlate at all.
Files
AudioDesyncFinder.pyw- 4.0.15
Audio Desync Finder 4.0.14
Audio Desync Finder 4.0.14
The Window field keeps what you type. The scan used to overwrite it with the block size on every run, so changing 80 to 30 snapped straight back to 80.
- the window is the correlation length and is independent of the block,
- it is only clamped — with a line in the log — when it would be longer than the block, because a window longer than the step averages the same audio twice.
Verified: Block 80 with Window 30 scans window 30 s and the field still says 30.
Files
AudioDesyncFinder.pyw- 4.0.14
Audio Desync Finder 4.0.13
Audio Desync Finder 4.0.13
The scan no longer freezes after "Measuring 74/74 blocks".
Two causes, both found by timing the same film:
waveform_offset()correlated a 30 s window against a 60 s search with a directnp.correlate— a trillion operations, minutes of work for a single jump. It uses an FFT correlation now.black_gaps(),transition_zones()andtarget_holes()each reshaped a whole track and averaged it per row — hundreds of megabytes per call — and they were called once per jump. They now shareframe_levels(), computed once per track, andlocate_cut()reuses one envelope pair per scan.
Measured on the goat pair:
before: 191.9 s (and the window looked hung)
after: 26.7 s
The chart, the table and the log fill in straight after the measuring phase.
Files
AudioDesyncFinder.pyw- 4.0.13
Audio Desync Finder 4.0.12
Audio Desync Finder 4.0.12
A model that has to insert silence exports now. It was refused because the first attempt drifted by 1.5 s. The cause was a duplicated second: every segment started at t_start + offset even when the target had already been played past that point, so the overlapping audio was played twice instead of being filled with silence.
_segments_to_sources() now keeps a cursor of the target already used, works out how much is missing before each segment, and the filter adds that silence at the end of the previous part.
Verified on both synthetic cases:
+3 s skip -> 236.6 s, residual 0 ms in the first half, 0 ms in the second
-3 s cut -> 240.0 s, residual 0 ms in the first half, 0 ms in the second
The export is no longer refused for padding.
Files
AudioDesyncFinder.pyw- 4.0.12
Audio Desync Finder 4.0.11
Audio Desync Finder 4.0.11
A cut without a fade is located by the step itself. locate_cut() measures a dozen seconds every six across three minutes either side of the boundary and finds the moment the level switches — for the cuts no transition marks.
Measured on the goat pair:
block grid boundary: 680 s (+50 -> -940 ms)
audio step found: 842 s (+50 -> -940 ms)
The cut you see at 10:05 is not an audio change: the correlation matches the first level (+50 ms) right through to 14:02. The segment the model built for 680–840 s rests on two blocks and reports RMS 0 — a line through two points is exact, which is exactly why it can put the boundary in the wrong place.
Files
AudioDesyncFinder.pyw- 4.0.11
Audio Desync Finder 4.0.10
Audio Desync Finder 4.0.10
The measurements around a cut are marked in the table.
For every boundary, the nearest measurement before it and the nearest after it get:
- a red dot in the first column,
- the kind
cut.
A fixed ±30 s window missed the neighbour on one side — the block grid is 80 s — so the nearest pair is used instead: those two are exactly what describes the step.
Files
AudioDesyncFinder.pyw- 4.0.10