Dear all,
I am currently rewriting several SOX sound exchange effects (
http://sox.sourceforge.net) as JSFX plugins for comparing interactive DAW rendering with external audio files rendered by the command-line SOX processor.
Those JSFX plugins should be more or less
bit exact. This works fine except for roundoff errors, because JSFX does floating point arithmetic, while SOX often uses integer arithmetic. Nevertheless those errors are at about -140dB, so they are neglectable.
Time invariant plugins are not really problematic, but there are also modulated plugins. To faithfully reproduce those externally rendered files the modulation phase of a jsfx has to be exact.
Unfortunately this does not work out of the box, because the modulation phase depends on the elapsed time in the current playback run.
E.g. assume a tremolo with a sine modulation of 0.25Hz and assume that the effect should process some audio at position 00:00:05.75 in the project. When you start playback at 00:00:05.75, everything is fine, when you start playback at 00:00:04.75, the modulation phase will be off by 90° at 00:00:05.75 (because the effect is already running for a second).
There is a trick to compensate that offset by using timelocking (and a relative phase) on the effect: when being in its init block, the effect can query "play_position" and find out how many modulation cycles have passed; the relative phase is a slider setting that tells the modulation phase at position 00:00:00.
Assume in our case that for some reason the modulation phase has to be at 90° when the effect starts at 00:00:05.75. Each second of "play_position" contributes 90° phase shift (360° * modulation_frequency) and the relative phase at 00:00:00 has to be at -67.5°. So the formula is
phaseShiftPerSecond = 360° * modulation_frequency;
startPhaseInInit = relativePhase + play_position * phaseShiftPerSecond;
Let's check this for our example:
- Phase shift is 360° * 0.25/s ≡ 90°/s.
- A start at 0: start phase is -67.5° + 0° ≡ -67.5°, after 5.75s the modulation phase is -67.5° + 5.75s * 90°/s ≡ 90°.
- A start at 3.5: start phase is -67.5° + 3.5s * 90°/s ≡ 247.5°. After another 2.25s, the modulation phase is 247.5° + 2.25s * 90°/s ≡ 90°
So this works fine whenever we start playback for the first time,
but it does not work when playback goes into a loop. You can have the JSFX re-inited at the begin of the loop, but even then it fails because typically that loop start position is not identical to the "play_position" (or at least has the same phase).
SO: is there some technique to make a JSFX phase correct (or also time correct)? Or possibly should "play_position" be updated to reflect the first position during the current playback?
Best regards,
DrTT