Float32Array
).
This specification defines methods that operate on entire arrays rather than single elements at a time. The methods are useful for implementing performance critical array processing, such as digital signal processing.
A fundamental principle of all methods defined in this specification is that they operate on existing arrays rather than creating new ones. Thus, it is possible to use the methods in applications that are sensitive to heap allocation and garbage collection.
The generic term TypedArray
is used to indicate any valid
typed array view type.
The term NaN
is short for Not a Number, and is a numeric
value representing an undefined or unrepresentable value.
The term RMS
is short for Root Mean Square, which is a
measure of signal energy. For a given signal it is calculated as the
square root of the arithmetic mean of the squares of the absolute
values of the signal.
The term single precision
means binary 32-bit IEEE 754
floating point.
The term double precision
means binary 64-bit IEEE 754
floating point.
Much of this specification is written in terms of mathematical expressions. In such expressions, the following pseudo function definitions MUST apply:
x
if xmin
≤ x
≤ xmax
.xmin
if x
< xmin
.xmax
if x
> xmax
.NaN
if any of x
, xmin
or xmax
is NaN
.
Returns x - floor(x/y) * y
.
x
< 0 or x
= -0.x
> 0 or x
= +0.NaN
if x
is NaN
.
Furthermore, each of the following pseudo functions MUST use the same
definition as the Math
object method with the same name,
as specified in [[!ECMA-262]], with the exception that single precision
arithmetic SHOULD be used instead of double precision arithmetic:
abs
,
acos
,
asin
,
atan
,
atan2
,
ceil
,
cos
,
exp
,
floor
,
log
,
min
,
max
,
pow
,
random
,
round
,
sin
,
sqrt
,
tan
.
Complex valued signals are represented as two separate arrays: one
containing the real part of the signal (suffixed Real
in
the IDL), and one containing the imaginary part of the signal (suffixed
Imag
in the IDL).
Unless otherwise noted, numerical computations that are defined in this specification MUST meet the following requirements:
The rationale is that computational performance is important. Conforming clients should be able to utilize as many optimization techniques as possible when implementing the API, including (but not limited to) using hardware specific instructions and data types that may or may not fully support IEEE 754.
As an example of reordering of operations, a conforming client may
choose to implement sqrt(x)
as
x * (1 / sqrt(x))
.
Overlapping operations are not allowed except for the special case where the destination array is the same as one of the source arrays (may differ for different methods).
k
in the range 0
≤ k
< min(dst.length, x.length, y.length)
,
the method MUST compute dst[k] = x[k] + y[k]
.
k
in the range 0
≤ k
< min(dst.length, y.length)
,
the method MUST compute dst[k] = x + y[k]
.
k
in the range 0
≤ k
< min(dst.length, x.length, y.length)
,
the method MUST compute dst[k] = x[k] - y[k]
.
k
in the range 0
≤ k
< min(dst.length, y.length)
,
the method MUST compute dst[k] = x - y[k]
.
k
in the range 0
≤ k
< min(dst.length, x.length, y.length)
,
the method MUST compute dst[k] = x[k] * y[k]
.
k
in the range 0
≤ k
< min(dst.length, y.length)
,
the method MUST compute dst[k] = x * y[k]
.
For each k
in the range 0
≤ k
< min(dstReal.length, dstImag.length, xReal.length, xImag.length, yReal.length, yImag.length)
,
the method MUST compute:
dstReal[k] = xReal[k] * yReal[k] - xImag[k] * yImag[k] dstImag[k] = xReal[k] * yImag[k] + xImag[k] * yReal[k]
For each k
in the range 0
≤ k
< min(dstReal.length, dstImag.length, yReal.length, yImag.length)
,
the method MUST compute:
dstReal[k] = xReal * yReal[k] - xImag * yImag[k] dstImag[k] = xReal * yImag[k] + xImag * yReal[k]
k
in the range 0
≤ k
< min(dst.length, x.length, y.length)
,
the method MUST compute dst[k] = x[k] / y[k]
.
k
in the range 0
≤ k
< min(dst.length, y.length)
,
the method MUST compute dst[k] = x / y[k]
.
For each k
in the range 0
≤ k
< min(dstReal.length, dstImag.length, xReal.length, xImag.length, yReal.length, yImag.length)
,
the method MUST compute:
denom = yReal[k] * yReal[k] + yImag[k] * yImag[k] dstReal[k] = (xReal[k] * yReal[k] + xImag[k] * yImag[k]) / denom dstImag[k] = (xImag[k] * yReal[k] - xReal[k] * yImag[k]) / denom
For each k
in the range 0
≤ k
< min(dstReal.length, dstImag.length, yReal.length, yImag.length)
,
the method MUST compute:
denom = yReal[k] * yReal[k] + yImag[k] * yImag[k] dstReal[k] = (xReal * yReal[k] + xImag * yImag[k]) / denom dstImag[k] = (xImag * yReal[k] - xReal * yImag[k]) / denom
k
in the range 0
≤ k
< min(dst.length, x.length, y.length, z.length)
,
the method MUST compute dst[k] = x[k] * y[k] + z[k]
.
k
in the range 0
≤ k
< min(dst.length, y.length, z.length)
,
the method MUST compute dst[k] = x * y[k] + z[k]
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = abs(x[k])
.
For each k
in the range 0
≤ k
< min(dst.length, xReal.length, xImag.length)
,
the method MUST compute
dst[k] = sqrt(xReal[k]2 + xImag[k]2)
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = acos(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = asin(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = atan(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length, y.length)
,
the method MUST compute dst[k] = atan2(y[k], x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = ceil(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = cos(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = exp(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = floor(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = log(x[k])
.
x
.
If the array is empty, it MUST return −∞.
x
.
If the array is empty, it MUST return ∞.
k
in the range 0
≤ k
< min(dst.length, x.length, y.length)
,
the method MUST compute dst[k] = pow(x[k], y[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = pow(x[k], y)
.
For each k
in the range 0
≤ k
< dst.length
,
the method MUST compute dst[k] = random() * (high - low) + low
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = round(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = sin(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = sqrt(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = tan(x[k])
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = clamp(x[k], xMin, xMax)
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = modulo(x[k], 1)
.
k
in the range 0
≤ k
< dst.length
,
the method MUST perform the assignment dst[k] = value
.
If dst
contains at least one element, the method MUST set the first element to first
.
For each k
in the range 1
≤ k
< dst.length
,
the method MUST compute dst[k] = first + k * ((last - first) / (dst.length - 1))
.
k
in the range 0
≤ k
< min(dst.length, x.length)
,
the method MUST compute dst[k] = sign(x[k])
.
The method MUST return the sum of all the elements of the array x
.
If the array is empty, it MUST return 0 (zero).
It is RECOMMENDED that the method is implemented using a summation algorithm with an accumulated round-off error growth equal to or better than that of pairwise summation.
This method implements linear interpolation of the source array
x
. The array is sampled at the points given in the array
t
, and stored in the destination array dst
.
Values in t
are clamped to the valid index range of
the array x
, i.e. [0, x.length)
.
The method MUST perform one of the following two operations:
x
is empty (has zero elements), the method
MUST set each element dst[k]
to zero, where
k
is in the range 0
≤ k
< min(dst.length, t.length)
.
x
has at least one element, the method
MUST perform the following operation for each k
in
the range 0
≤ k
< min(dst.length, t.length)
:
t' = clamp(t[k], 0, x.length - 1) idx = floor(t') w = t' - idx p1 = x[idx] p2 = x[min(idx + 1, x.length - 1)] dst[k] = (1 - w) * p1 + w * p2
This method implements linear interpolation of the source array
x
. The array is sampled at the points given in the array
t
, and stored in the destination array dst
.
Values in t
are taken modulo x.length
.
The method MUST perform one of the following two operations:
x
is empty (has zero elements), the method
MUST set each element dst[k]
to zero, where
k
is in the range 0
≤ k
< min(dst.length, t.length)
.
x
has at least one element, the method
MUST perform the following operation for each
k
in the range 0
≤ k
< min(dst.length, t.length)
:
t' = modulo(t[k], x.length) idx = floor(t') w = t' - idx p1 = x[idx] p2 = x[modulo(idx + 1, x.length)] dst[k] = (1 - w) * p1 + w * p2
This method implements cubic Catmull-Rom spline interpolation of the
source array x
. The array is sampled at the points
given in the array t
, and stored in the destination
array dst
.
Values in t
are clamped to the valid index range of
the array x
, i.e. [0, x.length)
.
The method MUST perform one of the following two operations:
x
is empty (has zero elements), the method
MUST set each element dst[k]
to zero, where
k
is in the range 0
≤ k
< min(dst.length, t.length)
.
x
has at least one element, the method
MUST perform the following operation for each
k
in the range 0
≤ k
< min(dst.length, t.length)
:
t' = clamp(t[k], 0, x.length - 1) idx = floor(t') w = t' - idx w2 = w*w w3 = w*w*w h1 = 2*w3 - 3*w2 + 1 h2 = -2*w3 + 3*w2 h3 = 0.5 * (w3 - 2*w2 + w) h4 = 0.5 * (w3 - w2) p1 = x[max(idx - 1, 0)] p2 = x[idx] p3 = x[min(idx + 1, x.length - 1)] p4 = x[min(idx + 2, x.length - 1)] dst[k] = h1 * p2 + h2 * p3 + h3 * (p3 - p1) + h4 * (p4 - p2)
This method implements cubic Catmull-Rom spline interpolation of the
source array x
. The array is sampled at the points
given in the array t
, and stored in the destination
array dst
.
Values in t
are taken modulo x.length
.
The method MUST perform one of the following two operations:
x
is empty (has zero elements), the method
MUST set each element dst[k]
to zero, where
k
is in the range 0
≤ k
< min(dst.length, t.length)
.
x
has at least one element, the method
MUST perform the following operation for each
k
in the range 0
≤ k
< min(dst.length, t.length)
:
t' = modulo(t[k], x.length) idx = floor(t') w = t' - idx w2 = w*w w3 = w*w*w h1 = 2*w3 - 3*w2 + 1 h2 = -2*w3 + 3*w2 h3 = 0.5 * (w3 - 2*w2 + w) h4 = 0.5 * (w3 - w2) p1 = x[modulo(idx - 1, x.length)] p2 = x[idx] p3 = x[modulo(idx + 1, x.length)] p4 = x[modulo(idx + 2, x.length)] dst[k] = h1 * p2 + h2 * p3 + h3 * (p3 - p1) + h4 * (p4 - p2)
src1
,
src2
, src3
and src4
,
and stores the data interleaved in the dst
array,
which may be of any Typed Array type.
This method MUST perform the following steps:
src2
, src3
or src4
, provided that it was passed as an argument,
has a length that differs from src1.length
, the method
MUST throw a "NotSupportedError" exception.
stride
is less than 1, the method MUST throw a
"NotSupportedError" exception.
dstCount
be floor((dst.length - offset) / stride)
.
k
in the range 0
≤ k
< min(dstCount, src1.length)
,
the method MUST perform the following operation, where type conversions
follow the rules of [[!WEBIDL]]:
dst[offset + stride*k] = src1[k]; if (src2 argument is defined) dst[offset + stride*k + 1] = src2[k]; if (src3 argument is defined) dst[offset + stride*k + 2] = src3[k]; if (src4 argument is defined) dst[offset + stride*k + 3] = src4[k];
src
,
which may be of any Typed Array type, and stores the de-interleaved
data in the arrays dst1
, dst2
,
dst3
and dst4
.
This method MUST perform the following steps:
dst2
, dst3
or dst4
, provided that it was passed as an argument,
has a length that differs from dst1.length
, the method
MUST throw a "NotSupportedError" exception.
stride
is less than 1, the method MUST throw a
"NotSupportedError" exception.
srcCount
be floor((src.length - offset) / stride)
.
k
in the range 0
≤ k
< min(srcCount, dst1.length)
,
the method MUST perform the following operation, where type conversions
follow the rules of [[!WEBIDL]]:
dst1[k] = src[offset + stride*k]; if (dst2 argument is defined) dst2[k] = src[offset + stride*k + 1]; if (dst3 argument is defined) dst3[k] = src[offset + stride*k + 2]; if (dst4 argument is defined) dst4[k] = src[offset + stride*k + 3];
A Filter
object implements FIR (finite impulse response)
and IIR (infinite impulse response) filters of any order.
The type and order of the filter is defined by setting the
a
and b
filter coefficients.
If a
is an empty array (if it has zero elements),
the object will effectively implement a convolution between the
signal x
and the impulse response b
.
A Filter
object maintains intra-call state in order to
facilitate continuous filter operation across several buffers.
A Filter
object MUST internally store filter coefficients
— hereafter referred to as the arrays a
(for the
recursive coefficients) and b
(for the convolution
coefficients).
A Filter
object MUST maintain a history state — hereafter
referred to as the history — of the
input and output signals. This state must keep enough data from previously
processed input and output signals to make the filtering function well
defined.
At a minimum, the history must contain the b.length - 1
last processed input samples and the a.length
last processed
output samples from previous calls to the filter()
method.
The Filter()
constructor, when invoked, MUST return a newly
created Filter
object. Furthermore, the constructor MUST
perform the following steps before returning the new Filter
object:
bSize
is less than 1, a
"NotSupportedError"
exception MUST be thrown.
b
of length bSize
,
where the first element is assigned the value 1, and the rest of the
elements are set to 0 (zero).
a
of length aSize
,
and set all the elements to 0 (zero).
clearHistory()
was called.
If there was not enough memory to create the Filter
object, a
"NotSupportedError"
exception MUST be thrown.
x
and stores the result in the array dst
.
The method MUST perform the following steps:
x
differs from the length of
dst
,
a "NotSupportedError" exception MUST be thrown.
for k = 0 to x.length - 1 do res = 0; for m = 0 to b.length - 1 do res = res + b[m] * x[k - m]; end for m = 0 to a.length - 1 do res = res - a[m] * dst[k - 1 - m]; end dst[k] = res; endFor negative indexes into the arrays
x
and dst
,
the internal history state from previous calls to this method
MUST be used.
min(b.length-1, x.length)
samples from the array
x
and the last min(a.length, dst.length)
samples from the array dst
.
filter()
call acts as if at least b.length - 1
input
samples with the value 0 (zero) have previously been processed, and
at least a.length
output samples with the value 0 (zero) have
previously been generated.
min(b.length, values.length)
coefficients of the internal b
array of the filter
object.
min(a.length, values.length)
coefficients of the internal a
array of the filter
object.
FFT
object implements forward and inverse discrete
Fourier transforms.
The FFT()
constructor, when invoked, MUST return a newly
created FFT
object.
If the constructor was passed an argument, then the FFT
object's size
property MUST be set to the value of that
argument. Otherwise, the object's size
property MUST be
set to 256.
If a value of less than 1 is passed as the argument to the FFT()
constructor, a "NotSupportedError"
exception MUST be thrown.
If there was not enough memory to create the FFT
object, a
"NotSupportedError"
exception MUST be thrown.
The size
property tells the number of frequency bins to
use in subsequent calls to the transformation methods of the
FFT
object: forward
, forwardCplx
,
inverse
and inverseCplx
.
This method MUST perform the same operation as if forwardCplx
was called using the following syntax:
forwardCplx (dstReal, dstImag, x, dummy)
,
where dummy
is a zero-filled Float32Array
of length size
.
This method MUST compute the forward Fourier transform of
the first size
elements of the complex input signal
(xReal
and xImag
), and store the result
in the first size
elements of the complex output signal
(dstReal
and dstImag
).
The generated output signal MUST be scaled such that the RMS of it equals the RMS of the input signal.
If any of the arrays xReal
, xImag
, dstReal
and dstImag
has less than size
elements, the method MUST throw a
"NotSupportedError" exception.
This method MUST perform the same operation as if inverseCplx
was called using the following syntax:
inverseCplx (dst, dummy, xReal, xImag)
,
where dummy
is a Float32Array
of length
size
.
This method MUST compute the inverse Fourier transform of
the first size
elements of the complex input signal
(xReal
and xImag
), and store the result
in the first size
elements of the complex output signal
(dstReal
and dstImag
).
The generated output signal MUST be scaled such that the RMS of it equals the RMS of the input signal.
If any of the arrays xReal
, xImag
, dstReal
and dstImag
has less than size
elements, the method MUST throw a
"NotSupportedError" exception.
Reading this attribute MUST return the size of the Fourier transform, i.e. the number of frequency bins that are used during a transform.
Below are some examples of how to use different interfaces and methods provided by this specification. Unless otherwise noted, they are written in ECMAScript (with [[!TYPED-ARRAYS]] support).
Some of the examples create temporary objects and arrays. In real world applications, it is expected that such objects are kept as members of long-lived objects in order to minimize heap allocation activity and garbage collection.
Generate a sine signal with 20 cycles:
var y = new Float32Array(1000); ArrayMath.ramp(y, 0, 2*Math.PI*20); ArrayMath.sin(y, y);
Generate a low-pass filtered noise signal, using a first order IIR filter:
var x = new Float32Array(1000); ArrayMath.random(x, -1, 1); var y = new Float32Array(x.length); var filt = new Filter(1, 1); filt.setB(new Float32Array([0.01])); filt.setA(new Float32Array([-0.99])); filt.filter(y, x); // y[k] = 0.01 * x[k] + 0.99 * y[k-1]
Calculate the RMS (root mean square) of a signal:
function rms (x) { var y = new Float32Array(x.length); ArrayMath.mul(y, x, x); return Math.sqrt(ArrayMath.sum(y) / y.length); }
Normalize a signal to a given peak amplitude:
function normalize (x, magnitude) { var peak = Math.max(Math.abs(ArrayMath.max(x)), Math.abs(ArrayMath.min(x))); if (peak > 0) ArrayMath.mul(x, magnitude / peak, x); }
Create a Hamming window:
function hamming (size) { var w = new Float32Array(size); ArrayMath.ramp(w, 0, 2*Math.PI); ArrayMath.cos(w, w); ArrayMath.mul(w, -0.46, w); ArrayMath.add(w, 0.54, w); return w; }