WebAudio / web-audio-api

The Web Audio API v1.0, developed by the W3C Audio WG
https://webaudio.github.io/web-audio-api/
Other
1.06k stars 168 forks source link

Does behavior of setTargetAtTime changes depending on (changes in) previous events? #2294

Closed qsjn4ukr closed 3 years ago

qsjn4ukr commented 3 years ago

According to the specification, the initial value, from which the exponentially approaching to the target value begins, is equal to the current value at the time of startTime parameter. Suppose we have a sequence of events SetTarget, SetTarget... SetTarget (currentTime is here, all the events in past) It is clear that we have to consider all the sequence of previous events to find initial value, from which the automation after the last SetTarget goes. What if we shoot now a setValueAtTime(value, startTime) with startTime less than startTime of the first event in the sequnce? Does this mean that we have to recalculate the whole sequence, change the initial value and change by jump the curve that is now rendered? Or this setValueAtTime be ignored just because it was called too late?

What needs to be clarified: Can the initial value of the setTarget event be calculated before the event actually happens, and can it be changed afterwards by changes in previous events.

It seems that a change (insertion) of an event that precedes LinearRampToValue or ExponentialRampToValue event should lead to an instant change of the ramp, even if that is already in acton. Should it? Is behavior of the SetTarget not similar to that of the Linear- or ExponentialRampToValue events?

https://webaudio.github.io/web-audio-api/#dom-audioparam-settargetattime

rtoy commented 3 years ago

One thing to keep in mind is the time for a linear or expoonential ramp is the end time of the event. For setTarget, it's the start time.

So if you have something like

t = context.currentTime;
setValueAtTime(0, t+1);
linearRampToValueAtTime(1, t+10);
// Wait 5 sec.
setValueAtTime(10, context.currentTime);

you should be a nice linear up ramp from time t+1 to time t+5. Then there's an instantaneous jump to 10 at time t+5. Then a linear ramp down to 1 until time t+10.

However, if you did something like:

t = context.currentTime;
setValueAtTime(10, t+1);
setTargetAtTime(0, t + 2);
setTargetAtTime(5, t + 10);
// Wait 5 sec.
setValueAtTime(3, context.currentTime);

the output would be a constant 10 until time t+2, at which you get a exponential approach to 0 starting at t+2. At time t+5, the value changes to 3 instantly (from wherever the setTarget value is), and stays at 3 until t+10 at which point an exponential approach to 5 begins.

Does this clarify things?

I don't think it's super clear, but the algorithm in Computation of Value plus the definitions of the events themselves makes the above examples behave as I said. (Assuming I didn't make a mistake.)

qsjn4ukr commented 3 years ago

t = context.currentTime; setValueAtTime(10, t+1); setTargetAtTime(0, t + 2); setTargetAtTime(5, t + 10); // Wait 5 sec. setValueAtTime(3, context.currentTime);

Question is: what if the last instruction is «setValueAtTime(3, t + 1.5);» instead?

rtoy commented 3 years ago

As the spec says, if the specified time is before the current time, it gets clamped to the current time. Hence setValueAtTime(3, t + 1.5) is the same as setValueAtTime(3, context.currentTime).

Here is a small example to be used with https://hoch.github.io/canopy:

// @duration 5
// @sampleRate 8000

let src = new ConstantSourceNode(context);
src.connect(context.destination);

src.offset.setValueAtTime(10, 0.1)
    .setTargetAtTime(0, .2, .2)
    .setTargetAtTime(5, 1, .2);
context.suspend(0.5)
    .then(() => {
      src.offset.setValueAtTime(3, context.currentTime, .2);
    })
    .then(() => context.resume());

src.start();

I divided the times by 10 so things happen sooner.

Unfortunately, this won't work in Firefox which hasn't implemented context.suspend yet. It might be possible to fake that but I'm too lazy to try.

qsjn4ukr commented 3 years ago

It seems that the word "clamped" did not fit on the screen when I read the specification. Everything is clear. The already runing SetTarget automation stops when method setValueAtTime/linearRampToValueAtTime/exponentialRampToValueAtTime with start/endTime <= currentTime is called. It is impossible to add an event in past.

rtoy commented 3 years ago

Thanks for confirming. We do want people to let us know if things could be explained better. It's always a hard trade-off because the spec is not a tutorial.