-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathooura.js
175 lines (143 loc) · 4.75 KB
/
ooura.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
const assert = require('assert');
const init = require('./init.js');
const trans = require('./transform.js');
class Ooura {
constructor(size, info = {type: 'real', radix: 4}) {
assert(Ooura.isPowerOf2(size));
this.real = (info.type === 'real');
if (!this.real) {
assert(info.type === 'complex'); // Sanity
}
this.size = size;
this.ip = new Int16Array(2 + Math.sqrt(size));
this.w = new Float64Array(size / 2);
this.internal = new Float64Array(size);
init.makewt(size / 4, this.ip.buffer, this.w.buffer);
// Perform additional modification if real
if (this.real) {
init.makect(size / 4, this.ip.buffer, this.w.buffer, size / 4);
this.fft = this.fftReal;
this.ifft = this.ifftReal;
this.fftInPlace = this.fftInPlaceReal;
this.ifftInPlace = this.ifftInPlaceReal;
} else {
this.fft = this.fftComplex;
this.ifft = this.ifftComplex;
this.fftInPlace = this.fftInPlaceComplex;
this.ifftInPlace = this.ifftInPlaceComplex;
}
}
// Returns complex vector size given one dimensional scalar size
static vectorSize(scalarSize) {
assert(Ooura.isPowerOf2(scalarSize));
return (scalarSize / 2) + 1;
}
// Inverse fucntion of vector size
static scalarSize(vectorSize) {
const result = (vectorSize - 1) * 2;
assert(Ooura.isPowerOf2(result));
return result;
}
static isPowerOf2(n) {
if (typeof n !== 'number') {
return false;
}
return n && (n & (n - 1)) === 0;
}
getScalarSize() {
return this.size;
}
getVectorSize() {
return Ooura.vectorSize(this.size);
}
// Helper factory functions returning correct array and data size for a
// given fft setup;
scalarArrayFactory() {
return new Float64Array(this.getScalarSize());
}
vectorArrayFactory() {
return new Float64Array(this.getVectorSize());
}
// Functions below here should be called via their aliases defined in the ctor
fftReal(dataBuffer, reBuffer, imBuffer) {
const data = new Float64Array(dataBuffer);
this.internal.set(data);
trans.rdft(this.size, trans.DIRECTION.FORWARDS, this.internal.buffer, this.ip.buffer, this.w.buffer);
const im = new Float64Array(imBuffer);
const re = new Float64Array(reBuffer);
// De-interleave data
let nn = 0;
let mm = 0;
while (nn !== this.size) {
re[nn] = this.internal[mm++];
im[nn++] = -this.internal[mm++];
}
// Post cleanup
re[this.size / 2] = -im[0];
im[0] = 0.0;
im[this.size / 2] = 0.0;
}
ifftReal(dataBuffer, reBuffer, imBuffer) {
const im = new Float64Array(imBuffer);
const re = new Float64Array(reBuffer);
// Pack complex into buffer
let nn = 0;
let mm = 0;
while (nn !== this.size) {
this.internal[mm++] = re[nn];
this.internal[mm++] = -im[nn++];
}
this.internal[1] = re[this.size / 2];
trans.rdft(this.size, trans.DIRECTION.BACKWARDS, this.internal.buffer, this.ip.buffer, this.w.buffer);
const data = new Float64Array(dataBuffer);
data.set(this.internal.map(x => x * 2 / this.size));
}
xfftComplex(direction, reIpBuffer, imIpBuffer, reOpBuffer, imOpBuffer) {
const reIp = new Float64Array(reIpBuffer);
const imIp = new Float64Array(imIpBuffer);
const reOp = new Float64Array(reOpBuffer);
const imOp = new Float64Array(imOpBuffer);
// Pack complex input into buffer
let nn = 0;
let mm = 0;
while (nn !== this.size) {
this.internal[mm++] = reIp[nn];
this.internal[mm++] = -imIp[nn++];
}
trans.cdft(this.size, direction, this.internal.buffer, this.ip.buffer, this.w.buffer);
// De-interleave data into output
nn = 0;
mm = 0;
while (nn !== this.size) {
reOp[nn] = this.internal[mm++];
imOp[nn++] = -this.internal[mm++];
}
}
fftComplex(reIpBuffer, imIpBuffer, reOpBuffer, imOpBuffer) {
this.xfftComplex(trans.DIRECTION.FORWARDS, reIpBuffer, imIpBuffer, reOpBuffer, imOpBuffer);
}
ifftComplex(reIpBuffer, imIpBuffer, reOpBuffer, imOpBuffer) {
this.xfftComplex(trans.DIRECTION.BACKWARDS, reIpBuffer, imIpBuffer, reOpBuffer, imOpBuffer);
const reOp = new Float64Array(reOpBuffer);
const imOp = new Float64Array(imOpBuffer);
for (let nn = 0; nn < this.size / 2; ++nn) {
reOp[nn] = reOp[nn] * 2 / this.size;
imOp[nn] = imOp[nn] * 2 / this.size;
}
}
// Below: No-nonsense thin wrappers around the interleaved in-place data
// representation with no scaling, for maximum throughput.
fftInPlaceReal(dataBuffer) {
trans.rdft(this.size, trans.DIRECTION.FORWARDS, dataBuffer, this.ip.buffer, this.w.buffer);
}
fftInPlaceComplex(dataBuffer) {
trans.cdft(this.size, trans.DIRECTION.FORWARDS, dataBuffer, this.ip.buffer, this.w.buffer);
}
ifftInPlaceReal(dataBuffer) {
trans.rdft(this.size, trans.DIRECTION.BACKWARDS, dataBuffer, this.ip.buffer, this.w.buffer);
}
ifftInPlaceComplex(dataBuffer) {
trans.cdft(this.size, trans.DIRECTION.BACKWARDS, dataBuffer, this.ip.buffer, this.w.buffer);
}
}
module.exports = Ooura;