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.05k stars 167 forks source link

Add automation using a power law #2415

Open delucis opened 8 years ago

delucis commented 8 years ago

The current exponentialRampToValueAtTime() method seems fairly shaky, not least because of its failure to accept ramps to/from zero, but also because the exponential curve it implements is not a quadratic curve.

The draft API states that “parameters representing filter frequencies and playback rate are best changed exponentially because of the way humans perceive sound,” which would suggest that a default exponential curve should be quadratic, complementing how frequency and amplitude scale perceptually.

What are the reasons for the current proposed model (below)?

image

Might it not be better to implement a single rampToValueAtTime() method, replacing both the linear and exponential ramp methods, which accepts a third optional argument for the exponent?

This could be implemented using the following (based on the existing model for linearRampToValueAtTime()), where _k_ is the exponent.

image

_k = 1_, resulting in a linear ramp (equivalent to linearRampToValueAtTime()) could be the default exponent, _k = 2_ would produce a quadratic curve, while _k < 1_ would produce logarithmic curves. _k = 4.2_ would come close to replicating the current exponentialRampToValueAtTime() function. Keyword aliases (“linear” => 1; “exponential” => 2) could also be a possibility.

image

rtoy commented 8 years ago

On Sat, Dec 5, 2015 at 8:14 PM, Chris Swithinbank notifications@github.com wrote:

The current exponentialRampToValueAtTime() http://webaudio.github.io/web-audio-api/#widl-AudioParam-exponentialRampToValueAtTime-AudioParam-float-value-double-endTime method seems fairly shaky, not least because of its failure to accept ramps to/from zero, but also because the exponential curve it implements is not a quadratic curve.

​Not sure what this means. It's called exponential because it's exponential, not quadratic.​

The draft API states http://webaudio.github.io/web-audio-api/#widl-AudioParam-exponentialRampToValueAtTime-AudioParam-float-value-double-endTime that “parameters representing filter frequencies and playback rate are best changed exponentially because of the way humans perceive sound,” which would suggest that a default exponential curve should be quadratic, complementing how frequency and amplitude scale perceptually.

What are the reasons for the current proposed model (below)?

[image: image] https://cloud.githubusercontent.com/assets/357379/11609919/8c389814-9b61-11e5-9711-6ea04844ad35.png

Might it not be better to implement a single rampToValueAtTime() method, replacing both the linear and exponential ramp methods, which accepts a third optional argument for the exponent?

​Well, frequencies are exponential, I think. You move up an octave and the frequency doubles. That's exponential. The frequencies for the notes on a piano are powers of two.​

This could be implemented using the following (based on the existing model for linearRampToValueAtTime() http://webaudio.github.io/web-audio-api/#widl-AudioParam-linearRampToValueAtTime-AudioParam-float-value-double-endTime), where k is the exponent.

[image: image] https://cloud.githubusercontent.com/assets/357379/11610981/1e578058-9b84-11e5-9327-ad8e33f717c8.png

k = 1, resulting in a linear ramp (equivalent to linearRampToValueAtTime()) could be the default exponent, k = 2 would produce a quadratic curve, while k < 1 would produce logarithmic curves. k = 4.2 would come close to replicating the current exponentialRampToValueAtTime() function. Keyword aliases (“linear” => 1; “exponential” => 2) could also be a possibility.

​I think your curve with k=4.2 only works because of the particular values of v0, v1, T1, and T0 that you used in the graph.

You can easily achieve what you want with setValueCurveAtTime, I think. ​

[image: image] https://cloud.githubusercontent.com/assets/357379/11611624/632df09c-9ba5-11e5-88f7-893dd980c40f.png

— Reply to this email directly or view it on GitHub https://github.com/WebAudio/web-audio-api/issues/671.

Ray

rtoy commented 8 years ago

If there are no additional comments, I would like to close this soon.

delucis commented 8 years ago

Apologies for the slow reply!

You’re right about the k=4.2 being dependent on particular values — my proposed function could not consistently replace the current exponential function.

However, I would still argue for something along the lines of a rampToValueAtTime() with optional exponent as I suggested above. setValueCurveAtTime() requires an array to define the curve, which could of course reproduce a non-linear ramp, but that is relatively clumsy. setValueCurveAtTime() seems more suited to envelopes and the like than ramps. (The MDN demo is just such an envelope.)

For example, the current exponentialRampToValueAtTime() is not perceptually smooth with regards to either gain or frequency ramping, a quadratic ramp would sound smoother. To achieve that with setValueCurveAtTime() would require an array (which is linearly interpolated between points, so you’d have to ensure enough points for longer ramp times). To achieve that with a ramp could be as simple as rampToValueAtTime(targetVal, targetTime, exponent) with the third argument optional (defaulting to 1, i.e. linear).

Does that make a bit more sense?

rtoy commented 8 years ago

Do you have an example where exponentialRampToValueAtTime() is not perceptually smooth?

I don't have any real objections to your proposal, except that it's now a feature request and not a bug in exponential ramps.

delucis commented 8 years ago

I’ll try to find time in the coming days to build a demo. I noticed it most with slower fade-ins: a linear ramp sounding loud too soon; the exponential ramp sounding loud too suddenly towards the end.

Agreed this is now a feature request.

rtoy commented 8 years ago

Thanks for looking into this. Perhaps setTargetAtTime will produce the kind of fade-in you want?

Anyway, marking this as a Feature Request/Missing Feature.

joeberkovitz commented 6 years ago

Note that arbitrary "laws" can be implemented using AudioWorkletNode piped into AudioParams.

khoin commented 5 years ago

Hello, I've just stumbled upon this.

Perhaps what @delucis is looking for is something like polynomialRampToValueAtTime().

The only use-case I can find right now is to automate equal-power cross-fading or panning.

Here's an existing example of equal-power cross-fading that is not automated: https://webaudioapi.com/samples/crossfade/ (Also mirrored on HTML5rocks: https://www.html5rocks.com/en/tutorials/webaudio/intro/#toc-xfade )

If one wanted to perform such automation, I believe it's currently possible with linearRampToValueAtTime(), or with setValueCurveAtTime() using correct values for the first argument's element.

Another solution-proposal to this problem without adding an extra method would be an extra argument for setValueCurveAtTime(). Right now, setValueCurveAtTime() interpolates linearly between values. Allowing a fourth argument which specifies the polynomial-degree of interpolation would solve this problem.

The example I linked above used a cosine function; however, a polynomial solution is also available (see: https://dsp.stackexchange.com/a/36778)

Examples of automation equal-power cross-fading:

// Assume gainNodeA.gain.value, gainNodeB.gain.value is current at 1 and 0 respectively

// With current specs
let curveLength = 20;
let curveA = (new Float32Array(curveLength)).fill(0).map((x,i) => (i/(curveLength-1))**0.5);
let curveB = curveA.reverse();

// This will crossfade between two signals within 2 seconds, linearly interpolating a curve
gainNodeA.gain.setValueCurveAtTime(curveA, blah.currentTime, 2);
gainNodeB.gain.setValueCurveAtTime(curveB, blah.currentTime, 2);

// --------------------
// Proposed ?
// using polynomialRampToValueAtTime(value, endTime, polynomialDegree)
gainNodeA.gain.polynomialRampToValueAtTime(0, blah.currentTime + 2, 0.5);
gainNodeB.gain.polynomialRampToValueAtTime(1, blah.currentTime + 2, 0.5);

// modified setValueCurveAtTime(values, startTime, duration, polyDegree)
gainNodeA.gain.setValueCurveAtTime([1.0, 0.0], blah.currentTime, 2, 0.5);
gainNodeB.gain.setValueCurveAtTime([0.0, 1.0], blah.currentTime, 2, 0.5);

Considerations

Misc.

padenot commented 4 years ago

Virtual F2F:

rtoy commented 4 years ago

Teleconf: We should do this, especially if it's common as Paul says in https://github.com/WebAudio/web-audio-api-v2/issues/22#issuecomment-642076903.

rtoy commented 4 years ago

Proposal:

AudioParam.powerRampToValueAtTime(v1, t1, k)

Same parameters and order like linearRampToValueAtTime, with the an additional 3rd parameter, k, that specifies the power law. The formula would be the one given in https://github.com/WebAudio/web-audio-api-v2/issues/22#issue-494354028.

k should be constrained to be non-negative perhaps?

rtoy commented 3 years ago

F2F meeting: API in https://github.com/WebAudio/web-audio-api-v2/issues/22#issuecomment-705848406 is good. Nothing more needed except writing up the spec.