Log a note for loops which audibly click at their wrap-around point - #340
Log a note for loops which audibly click at their wrap-around point#340douglas-carmichael wants to merge 2 commits into
Conversation
Sample libraries ship loops whose boundaries land on non-matching sample values surprisingly often - 17 of 152 presets across three commercial Ensoniq libraries - and without a note the first hint is the converted preset ticking on the destination device, where finding the culprit means listening to every preset. After the processing of each source, every forward loop without a cross-fade is now checked: the step from the last played frame back to the loop start is compared with the median frame-to-frame movement of the waveform around the loop boundaries. A step of at least eight times that movement and at least 2% of the local level - thresholds calibrated on 3527 looped samples of the three libraries - is heard as a tick on every repeat and logs one aggregated note per multi-sample, with the worst zone and its step as a percentage of the level. Nothing is changed: the loop is written as the source authored it, and the note points to the snap-to-zero-crossing and loop cross-fade processing options which remove such clicks. The measurement reuses the discontinuity function of the loop snapper, so the note and the snap option can never disagree about a loop; a loop the snap option fixed is therefore not reported. The check runs for conversion and analysis but not for the contents scan, and converting a full library of 514 presets measures the same wall time with and without it.
The step at the wrap was compared with the median frame-to-frame movement of the waveform around the loop boundaries. That comparison depends on the sample rate of the source: the same waveform stored at 22 kHz moves twice as much per frame as at 44.1 kHz, so a step which audibly sticks out once the destination format resamples the audio to a higher rate could hide below the threshold in the source file. Across 821 loops converted to 44.1 kHz, the source-rate check missed 4 of the 16 loops which click in the written files - all of them low-rate sources (16-30 kHz). The median movement is now scaled to a 44.1 kHz reference before the comparison, which makes the check independent of the source rate and catches 14 of the 16. Resampling itself does not need to be simulated: measured across the same 821 loops, the step as a part of the level stays within one percent point through resampling - only the per-frame yardstick was changing, never the step.
|
One refinement after measuring the check against what actually lands in the written files: the movement comparison is now made per unit of time instead of per frame (second commit). The step at the wrap was compared with the median frame-to-frame movement around the loop boundaries, and that yardstick depends on the source sample rate - the same waveform stored at 22 kHz moves twice as much per frame as at 44.1 kHz. A step which audibly sticks out after the destination resamples to a higher rate could therefore hide below the threshold in a low-rate source. Comparing all 821 loops of the three Ensoniq libraries in both domains (source audio vs the written 44.1 kHz files):
With the median movement scaled to a 44.1 kHz reference the check catches 14 of the 16; the three additional reports are loops whose source step is large (up to 47% of the level) but which happen to land cleanly at this particular destination rate - they are broken as authored, so reporting them seems right. The two remaining misses are wraps which only become steep through the frame relocation of resampling, which is not predictable from the source audio. |
Sample libraries ship loops whose boundaries land on non-matching sample values surprisingly often. Converting three commercial Ensoniq libraries (DSF ASR/MR/ZR, 152 selected presets), 17 presets click audibly at their loop wrap - steps of up to 63% of the sample level, a tick on every repeat of a held note. Nothing in the log points at them today: the first hint is the converted preset ticking on the destination hardware, and finding the culprits means listening to every preset one by one.
This adds one aggregated note per affected multi-sample:
How it decides. After the processing of each source, every forward loop without a cross-fade is checked: the step from the last played frame back to the loop start is compared with the median frame-to-frame movement of the waveform around the loop boundaries. A step of at least 8 times that movement and at least 2% of the local level is reported. The thresholds were calibrated against 3527 looped samples of the three libraries - loud enough to be heard on sustained material, quiet enough that none of the ~1900 clean loops is flagged.
What it deliberately does not do:
discontinuityfunction ofLoopZeroSnapper(now package-visible), so the note and the snap option can never disagree about a loop: a loop the snap option fixed is not reported. Converting with snapping enabled shrinks the notes to exactly the loops snapping could not fix (the pitch-critical short loops it skips), which tells the user those need a cross-fade instead.Cost: the audio of a zone is only read when it has an uncrossfaded forward loop, and once per zone. Converting the full MR library (514 presets, 5095 samples) measures the same wall time with and without the check (8.5s vs 9.6s across runs - inside run-to-run noise, since decoding is a fraction of the resample/write work the conversion does anyway).
The check has already caught a real bug once: while validating it against a build without #338, it flagged 'Eastern EPno' at 92% - the wrong-library sample that PR fixes. With the note in place, that class of problem is visible in the log instead of on the hardware.