Transfer function of WaveShaperNode - javascript

I'm having trouble understanding how the transfer function for a WaveShaperNode in the Web Audio API works. As I understand, a transfer function is a waveshaper which takes in a signal input x and produces a new signal y. So,
y = f(x)
I understand that if x equals zero, then so should y. Therefore, 0 = f(0). And that to fit in an appropriate range, y should be between [-1, 1], so this function: y = x / (1 + |x|) limits the output range to [-1, 1]. And that Chebyshev Polynomials are useful for transfer functions used to "distort" musical input signals.
But for a transfer function you need the input signal x in order to manipulate it and create an output y. However, with a WaveShaperNode in the Web Audio API, you don't have access to the original input x (or do you?). Often I see algorithms like:
for(var i = 0; i < sampleRate; i++){
var x = i * 2 / sampleRate - 1;
this.curve[i] = (3 + distortionAmount) * x * 20 * (Math.PI / 180) / (Math.PI + distortionAmount * Math.abs(x));
}
Where, in the above code, this.curve is a Float32Array representing the graphing of each sample frame. And I assume x here is supposed to represent the input audio signal. Yet, it doesn't actually represent the exact input audio signal. Is this because it just represents an average sinusoid and the actual input doesn't matter? Does the WaveShaperNode take the original input x and use (multiply?) the general curve we created to calculate the output y?

The WaveShaper node does not enable generic transfer functions, per se - but you can use it to do that. To answer your question - x is the offset into the array, with a little preprocessing.
The curve you give to the waveshaper is like a lookup table for x - spread across the range [-1,1]. Y does not need to be in [-1,1] - but x does. So, to solve f(x) for x in [-1,1], you just get the value at
curve[ Math.floor( (x+1)/2 * (curve.length-1) ) ];
or something like that.
It's not actually true that if x equals zero, then so must y; but it's likely. You could use the waveshaper to implement a DC offset, for example.
That "sampleRate" bit in your demo code is goofy - you should use maxint, not samplerate, to determine the size of your array. sampleRate is a time-domain thing, and waveshaper only operates in the amplitude vector.

The WaveShaperNode is described as applying a non-linear distortion. As such it would not have a classical linear time-invariant transfer function (which strictly only applies to linear distortions).

Related

Get opposite offset points from a root

With root-finding algorithms, you're usually just given the x value of the position of a root. I want to get the two closest points on either side of the root where these two points would have opposite signs considering that's where the roots exist (where the y value goes from + to - or the other way around)
So given an x value that is the approximate result of a root within an interval, what's the best efficient method to find the nearest two points to that root that have opposite y values.
Currently, my approach is to keep widening the offset from the roots position on either side until the y values of these two points are opposite.
Also, maybe using the precision (like 1e-6) that was used to find the root could be helpful to determine how much of an offset is needed to get passed the actual root where the signs change!
This image is sort of what I want. Blue are the two points I want that are at either side of the approximate root (which is purple). Of course the closer they are the better (without using further more iterations to get a more precise root, but instead only use the root given - we can assume that it's close enough).
I know some algorithms like bisection give the last closest brackets that could be used as these points unless the 1st iteration found the root exactly at the midpoint and the brackets are still far apart. While I'm using ridders (regula falsi) method which has a bias so one bracket usually doesn't move.
Code
let x = 3.135, y = fn(x); /* x = approximate result of root, actual root is pi */
let xmin = 3.1, xmax = 3.2;
let offset = (xmax - xmin) * .0000000002;
function getOffsetPoints(x, y, offset, fn) {
/* if root exactly 0 */
/* offset either side by 1e-15 = .000000000000001! */
if (y === 0) {
return [
[x - 1e-15, fn(x - 1e-15)],
[x + 1e-15, fn(x + 1e-15)]
]
}
let dxLeft = x - offset,
dyLeft = fn(dxLeft),
dxRight = x + offset,
dyRight = fn(dxRight);
/* maybe 3 attempts is enough - don't want to over do it with computations */
let i = 0;
while (i <= 3 && Math.sign(dyLeft) * Math.sign(dyRight) !== -1) {
offset *= 100;
dxLeft = x - offset;
dyLeft = fn(dxLeft);
dxRight = x + offset;
dyRight = fn(dxRight)
i++;
}
/* return two points at either side of root */
return [
[dxLeft, dyLeft],
[dxRight, dyRight]
]
}
function fn(x) {
return Math.tan(x);
}
EDIT: Forgot to mention that it's assumed the approximate root was already found within the interval xmin and xmax, and it does exist somewhere between that interval.
So the maximum interval will be the distance from xmin to xmax.
I guess this question is subtle as there is a trade-off between closeness (precision) and iterations (performance), and overall I'm looking for something more optimal than what I have
I presume that you refer to the floating-point representation of the numbers (because for a continuous function, the "nearest" points do not exist).
Beware that very close to a root the function evaluation loses any reliability and might not even be monotonous and there could be several nearby changes of signs, making the "root" not unique. None of the usual methods, except dichotomy, are even guaranteed to land close to a change of sign !
Anyway, if you want the nearest point of a different sign (chances of an exact zero are pretty low), you can explore on either sides by incrementing/decrementing the lower-order bits of the floating-point mantissa and evaluate the function. The best is to map an integer on the FP representation.
Values that cause a change in the exponent will need special handling (though for first experiments you can probably ignore them).

get a point on a bezier curve without guessing or brute force

I originally wanted to use four points (as a bezier Curve is defined with 4 points), but that forces me to brute force the position, so I tried a different approach i now need help with:
I have a start point P0, an end point P1 and slopes m0 and m1 which are supposed to give me the start/end slope to calculate a Bezier Curve inbetween them.
The Curve is supposed to be in the form of a function (3rd degree), since I need to get the height y of a given point x.
Using the HTML5Canvas i can draw a bezier curve no problem and using this function
that allows me to calculate any given point given a percentage of the way i can get the center point of the curve. But I don't need it depending on t but rather the y depending on x, so not halfway of the curve but halfway of the x distance between P0 and P1.
Image to visualize:
Left is what i can calculate, right is what i need.
I've been trying to calculate the cubic function given the two points P0, P1 as well as the slopes m0, m1, which results into four equations which i can't seem to be able to solve with only variable inputs. I've also tried to use the above function to calculate the t using the x value (which is known), but no dice there either.
I need to avoid using approximations or costly loops for these calculations as they are performed many times a second for many objects, thus this answer is not feasible for me.
Any help is appreciated.
I've encountered the same problem in a project I'm working on. I don't know of a formula to get the y coordinate from the x, and I suspect you'll have trouble with that route because a bezier curve can have up to 3 points that all have the same x value.
I would recommend using the library BezierEasing, which was designed for this use case and uses various performance enhancing techniques to make lookups as fast as possible: https://github.com/gre/bezier-easing
To solve this problem, you need to rewrite Bezier equation in power polynomial form
X(t) = t^3 * (P3.X-3*P2.X+3*P1.X-P0.X) +
t^2 * (3*P0.X + 6*P1.X+3*P2.X) +
t * (3*P1.X - 3P2.X) +
P0.X
if X(t) = P0.X*(1-ratio) + P3.X*ratio
then
let d = ratio * (P0.X - P3.X)
and solve cubic equation for unknown t
a*t^3 + b*t^2 + c*t + d = 0
JS code here
Then apply calculated t parameter (there might be upto three solutions) to Y-component and get point coordinates. Note that formulas are close (no loops) and should work fast enough
Thank you to everyone that answered before, those are generally great solutions.
In my case I can be 100% sure that I can convert the curve into a cubic function, which serves as the approximation of the bezier curve using the result of this calculation.
Since i have control over my points in my case, I can force the P0 to be on x=0, which simplifies the linear system calculations and thus allows me to calculate the cubic function much easier like this:
let startPoint: Utils.Vector2 = new Utils.Vector2(0, 100);
let endPoint: Utils.Vector2 = new Utils.Vector2(100, 100);
let a: number, b: number, c: number, d: number;
function calculateFunction() {
let m0: number = 0;
let m1: number = 0;
a = (-endPoint.x * (m0 + m1) - 2 * startPoint.y + 2 * endPoint.y) / -Math.pow(endPoint.x, 3);
b = (m1 - m0 - 3 * a * Math.pow(endPoint.x, 2)) / (2 * endPoint.x);
c = m0;
d = startPoint.y;
}

Trying to model a hole being drilled using JS/Canvas

I'm trying to represent a hole being drilled using a web based application and I'm having difficulties. I'm in control of the inputs and various variables but unsure of how best to approach the issue.
The simulation currently has these values, they're for test purposes only.
Radius of drill bit= 15
inches Length of drill = 1000ft
RPM of drill = 100
The stratigraphic layers have their own properties, and in this instance have:
Name = bla
Depth = 100ft (models start and finish of each layer - here, 0 to 100ft down)
Permeability = 10 (currently unsure how best to model)
I don't know at the moment how to model the pressure being applied to the drill but a constant can be used if need be.
I thought I'd be able to calculate the volume of the drillbit and then apply a percentage of sorts that would represent the strength of the material so as to slow the progress of the drill.
In it's simplest form I'm trying to figure out how best to represent a hole being drilled and then calculate the area of the hole as it's being drilled.
Here's my test code:
The time variable is passed in my the way of a JavaScript Date() object. I'm hoping on using the Date object to represent the actual amount that may be drilled real time.
The test case of the canvas that is modelling it is 800px deep with a well depth of 20000ft = 25ft per pixel. I'm still trying to make sense of the output and correct it, it's a slow process. Below is the rudimentary test code.
// aggregate function:
// depth(t) = (a * RPM - b * density) * t
function depthOverTime(time, density, a, b){
var aa = (a * RPM - b * density) * time;
//(1 * 50 - 1 * 20) * 60
//(50 - 20) * 60
//30 * 60
//1800
console.log("DOT: " + roundTo2(aa)+"ft^3" + "T: " +time);
return aa;
}
function volumeExcavated(t){
var rad = 15 * 0.083333;
dot = depthOverTime(t,20,1,1);
var a = Math.PI * Math.pow(rad, 2) * dot;
console.log("VEOT: " + roundTo2(a)+"ft^3");
return a;
}
This is a sample of the console log: http://pastebin.com/UW1M73jY
As a real world simulation, this isn't nearly enough information to come up with a code description. So, assuming this is a purely fictional exercise, as a simple simulation the hole you're drilling is always going to have the drill's penetrated volume, with the drill's speed of penetration determined by the substrate's density (ignoring a million real world material properties). The RPM value determines how much substrate gets scraped per time unit, so density slows your penetration down, RPM speeds your penetration up, and if we assume the drill is indestructible, that's pretty much all we need to know.
The volume excavated is equal to the drill's volume, ignoring its groove. Keep things simple and assume a cylindrical drill, and the volume is simply πr²h, where h is the penetrated depth, and r is (obviously) the drill radius.
Start with an aggregate function:
depth(t) = (a * RPM - b * density) * t
we don't know a or b, but we do know what they represent: a is the pressure behind our drill, and b is an unknown constant that balances our function. The first step is to normalise this function with respects to the various units (RPM is per minute, t is most likely in seconds, for instance), after which we can start figuring out what a and b should concretely be (also note that we can normalise this function by fixing either a or b to 1, and setting the remaining free variable to whatever is necessary to balance the equation with respect to the reality we're simulating)
Once we have our depth-over-time function, we're pretty much done, as the volume excavated is simply
volume(t) = π * r² * depth(t)
or, if you're a student of physics and prefer the common quadratic expression:
volume(t) = τ/2 * r² * depth(t)

Select optimal set of samples to approximate a curve with predetermined number of samples?

Background
I have a pet project that I love to overthink from time to time. The project has to do with an RC aircraft control input device. People familiar with that hobby are probably also familiar with what is known as "stick expo", which is a common feature of RC transmitters where the control sticks are either more or less sensitive near the neutral center position and become less or more sensitive as the stick moves closer to its minimum or maximum values.
I've read some papers that I don't fully understand. I clearly don't have the math background to solve this, so I'm hoping that perhaps one of you might.
Problem
I have decided to approximate the curve by taking a pre-determined number of samples and use linear interpolation to determine output values for any input values between the sample points. I'm trying to find a way to determine the most optimal set of sample points.
If you look at this example of a typical growth curve for this application, you will notice that some sections are more linear (straighter), and some are less linear (more curvy).
These samples are equally distant from each other, but they don't have to be. It would be smart to increase the sample density where there is more change and thereby increasing the resolution in the curvy segments by borrowing redundant points from the straight segments.
Is it possible to quantify the degree of error? If it is, then is it also possible to determine the optimal set of samples for a given function and a pre-determined number of samples?
Reference Code
Snippet from the class that uses a pre-calculated set of points to approximate an output value.
/* This makes the following assumptions:
* 1. The _points[] data member contians at least 2 defined Points.
* 2. All defined Points have x and y values between MIN_VALUE and MAX_VALUE.
* 3. The Points in the array are ordered by ascending values of x.
*/
int InterpolatedCurve::value( int x ) {
if( _points[0].x >= x ) { return _points[0].y; }
for( unsigned int i = 1; i < _point_count; i++ ) {
if( _points[i].x >= x ) {
return map(x, _points[i-1].x, _points[i].x,
_points[i-1].y, _points[i].y);
}
}
// This is an error condition that is not otherwise reported.
// It won't happen as long as the points are set up correctly.
return x;
}
// Example map function (borrowed from Arduino site)
long map( long x, long x1, long x2, long y1, long y2 ) {
return (x - x1) * (y2 - y1) / (x2 - x1) + y1;
}
Although my project is actually in C++, I'm using a Google spreadsheet to produce some numbers while I ponder this problem.
// x: Input value between -1 and 1
// s: Scaling factor for curve between 0 (linear) and 1 (maximum curve)
// c: Tunable constant
function expo_fn( x, s, c ) {
s = typeof s !== 'undefined' ? s : 1.0;
c = typeof c !== 'undefined' ? c : 4.0;
var k = c * ((c - 1.0) * s*s*s + s)/c + 1.0;
return ((k - 1.0) * x*x*x*x*x + x)/k;
};
The following creates a set of isometrically distributed (non-optimal) points between input values -1 and 1. These output values were expanded to integers between -16383 and 16383 for the above example spreadsheet. Factor is a value between 0 and 1 that determines the "curviness"--zero being a flat, linear curve and 1 being the least-linear curve I care to generate.
function Point( x, y ) {
this.x = x;
this.y = y;
};
function compute_points_iso( count, factor ) {
var points = [];
for( var i = 0; i < count; ++i ) {
var x = 2.0/(count - 1.0) * i - 1.0;
var y = expo_fn(x, factor);
points.push(new Point(x,y));
}
return points;
};
Relevant Academic Work
I have been studying this paper describing an algorithm for selecting significant data points, but my program doesn't quite work right yet. I will report back if I ever get this thing working.
The key here is to realize that you can bound the error on your linear interpolation in terms of the second derivative of the function. I.e. if we approximate f(x) \approx f(x_0) + f'(x_0)*(x-x_0), then the error in this approximation is less than abs[ 0.5*f''(x_0)(x-x_0)^2 ].
The outline of an iterative approach could look like this:
Construct an initial, e.g. uniformly spaced, grid
Compute the second derivative of the function on this grid.
Compute the bound on the error using the second-derivative and the inter-sample spacing
Move the samples closer together where the error is large; move them further apart where the error is small.
I'd expect this to be an iterative solution that loops on steps 2,3,4.
Most of the details are in step 4.
For a fixed number of sample points one could use the median of the error bounds to select
where finer/coarser sampling is required (i.e. those locations where the error is larger than the median error will have the sample points pulled closer together).
Let E_0 be this median of the error bounds; then we can, for each sample in the point, compute a new desired sample spacing (dx')^2=2*E_0/f''(x); then you'd need some logic to go through and change the grid spacing so that it is closer to these ideal spacings.
My answer is influenced by having used the "Self-Organizing Map" algorithm on data; this or related algorithms may be relevant for your problem. However, I can't recall ever
seeing a problem like yours where the goal is to make your estimates of the error uniform across the grid.

finding value of theta using the equation of circle

Using the equation of circle I have
x = rCos(theta)
y = rSin(theta)
Now I want to calculate theta. I have
x = -87.91
r = 14.63
what will be the value of theta? using
Math.acos(x/r)
its giving me NaN...
From MDN
The acos method returns a numeric value between 0 and pi radians for
x between -1 and 1. If the value of number is outside this range, it
returns NaN.
You need to get it within the range.
If you have x = -87.91, than r >= 87.91, so you have something wrong.
The best method in such situations is usually theta = Math.atan2(y, x) http://en.wikipedia.org/wiki/Atan2
sine and cosine don't have true inverses unless you restrict their range. So the acos and asin methods are only defined over these restricted ranges (which become the domains of asin and acos).
You have to ensure that theta is a value in radians, and that sentiment has been echoed thoroughly. If you don't want to convert values before you enter them, then you can use a conversion formula while calculating x and y.
x = rCos(theta * (3.14/180))
y = rSin(theta * (3.14/180))

Categories