Old 08-02-2019, 02:14 PM   #1
DrTT
Human being with feelings
 
Join Date: Jan 2019
Posts: 66
Default How to make phase correct / time correct JSFX plugin?

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

Last edited by DrTT; 08-02-2019 at 04:05 PM.
DrTT is offline   Reply With Quote
Old 09-04-2019, 12:22 PM   #2
DrTT
Human being with feelings
 
Join Date: Jan 2019
Posts: 66
Default How to make phase correct / time correct JSFX plugin?

Dear all,

sorry to answer my own questions, but this might be interesting for others.

As mentioned in the original question a modulated effect like e.g. a phaser produces a different sound for different start times because the modulation is normally in another phase.

Hence when looking at the behaviour at a specific point in time, those time-variant effects would behave differently when the effect start time is varied.

E.g., assume a phaser with a 0.25Hz modulation (one cycle every 4s): when you start the effect 1s later, its modulation is now off by 90°. This is not helpful when the
effect now depends on start time or loop positioning.

Ideally the effect should check the current play position and always behave the same at some specific point in time regardless of the playback start time.

The solution technique is quite simple:
  • Introduce a variable previousPlayPosition and initialize it to infinity in the slider section.
  • In the sample section check whether play_position is less than previousPlayPosition. If yes, reset the effect (depending on play_position); in any case set previousPlayPosition to play_position.
  • Additionally those effects should have a (slider) parameter timeOffset. This parameter tells at what time the effect has a time/phase of zero in its modulation or processing. The default is 0s, but it may be adapted accordingly (e.g. when the phase/time has to be 0 at some other position of the song).
In JSFX code this is:
Code:
@slider
    previousPlayPosition = 1E99;
    ...

@sample
    play_position < previousPlayPosition ? (
        effectTime = play_position - timeOffset;
	«initialize effect with effectTime»
    );

    previousPlayPosition = play_position;
    ...
The impact on sample processing time is minimal for normal circumstances (one failed condition check and an assignment), there is some time penalty when a loop is
taken, but the approach works fine.

Hope this helps!

Best regards,

DrTT
DrTT is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 02:45 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.