-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathresample.js
111 lines (94 loc) · 3.69 KB
/
resample.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
"use strict"
var fft = require("ndarray-fft")
var pool = require("ndarray-scratch")
var ops = require("ndarray-ops")
var cwise = require("cwise")
var clampScale = cwise({
args:["array", "array", "scalar", "scalar", "scalar"],
body: function clampScale(out, inp, s, l, h) {
var x = inp * s
if(x < l) { x = l }
if(x > h) { x = h }
out = x
}
})
function resample(out, inp, clamp_lo, clamp_hi) {
if(typeof clamp_lo === "undefined") {
clamp_lo = -Infinity
}
if(typeof clamp_hi === "undefined") {
clamp_hi = Infinity
}
var ishp = inp.shape
var oshp = out.shape
if (inp.shape.length !== out.shape.length) throw new Error("ndarray-resample: input and output arrays should have the same dimensions")
var v, zeroInds = ishp.map(function(){return 0})
if(out.size === 1) {
v = ops.sum(inp)/inp.size
if(v < clamp_lo) { v = clamp_lo }
if(v > clamp_hi) { v = clamp_hi }
out.set.apply(out, zeroInds.concat(v))
return
} else if (inp.size === 1) {
v = inp.get.apply(inp, zeroInds)
if(v < clamp_lo) { v = clamp_lo }
if(v > clamp_hi) { v = clamp_hi }
ops.assigns(out, v)
return
}
var d = ishp.length
var mshp = new Array(d), initToZero = false
for(var i=0; i<d; i++) {
mshp[i] = Math.min(oshp[i], ishp[i])
if (oshp[i] > ishp[i]) initToZero = true // When upsampling, initialize the Fourier components of the output to zero
}
var x = pool.malloc(ishp)
, y = pool.malloc(ishp)
ops.assign(x, inp)
ops.assigns(y, 0.0)
fft(1, x, y)
var lo = x.lo
, hi = x.hi
var s = pool.malloc(oshp)
, t = pool.malloc(oshp)
if (initToZero) {
ops.assigns(s, 0.0)
ops.assigns(t, 0.0)
}
var nr = new Array(d)
, a = new Array(d)
, b = new Array(d)
, io = new Array(d)
for(var i=0; i<1<<d; ++i) { // Iterate over the 2^d regions resulting from splitting up the indices in low frequencies above zero and low frequencies below zero (which turn up at the end of the arrays)
for(var j=0; j<d; ++j) { // Iterate over dimensions to determine correct starting indices and lengths
if(!(i&(1<<j))) { // Take the positive frequencies for this dimension
nr[j] = (mshp[j]+1)>>>1 // Take ceil(mshp[j]/2)) low frequencies (for example [0,1] for both mshp[j]==3 and mshp[j]==4)
a[j] = 0
b[j] = 0
io[j] = 0
} else { // Take the negative frequencies for this dimension
nr[j] = mshp[j] - ((mshp[j]+1)>>>1) // Take the rest ([-1] for mshp[j]==3, and [-2,-1] for mshp[j]==4)
if(nr[j] === 0) {
continue
}
a[j] = oshp[j] - nr[j]
b[j] = ishp[j] - nr[j]
// If mshp[j] is even, set the first imaginary values (along this dimension) to zero.
// For example, if mshp[j]==4, 2 and -2 correspond to the same frequency, and should be the average of the amplitudes for 2 and -2.
// Since the input is real, the Fourier transform has Hermitian symmetry, and we can simply take one or the other and set the corresponding imaginary coefficient(s) to zero.
// Note that when upsampling, this means that we get a asymmetric response (for example, -2, but not 2 has a non-zero weight), but this does not matter, since the weight is real anyway (again, given Hermitian symmetry).
io[j] = (mshp[j]&1) ? 0 : 1
}
}
ops.assign(hi.apply(lo.apply(s, a), nr), hi.apply(lo.apply(x, b), nr))
ops.assign(lo.apply(hi.apply(lo.apply(t, a), nr), io), lo.apply(hi.apply(lo.apply(y, b), nr), io))
ops.assigns(hi.apply(hi.apply(lo.apply(t, a), nr), io), 0.0)
}
fft(-1, s, t)
clampScale(out, s, out.size/inp.size, clamp_lo, clamp_hi)
pool.free(x)
pool.free(y)
pool.free(s)
pool.free(t)
}
module.exports = resample