Passed
Branch v11.0.x (f62822)
by Rafael S.
03:04
created

Interpolator.constructor   A

Complexity

Conditions 4

Size

Total Lines 40
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 13
dl 0
loc 40
rs 9.75
c 0
b 0
f 0
1
/*
2
 * Copyright (c) 2019 Rafael da Silva Rocha.
3
 * Copyright 2012 Spencer Cohen
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining
6
 * a copy of this software and associated documentation files (the
7
 * "Software"), to deal in the Software without restriction, including
8
 * without limitation the rights to use, copy, modify, merge, publish,
9
 * distribute, sublicense, and/or sell copies of the Software, and to
10
 * permit persons to whom the Software is furnished to do so, subject to
11
 * the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be
14
 * included in all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
 *
24
 */
25
26
/**
27
 * @fileoverview The Interpolator class. Based on Smooth.js by Spencer Cohen.
28
 * @see https://github.com/rochars/wavefile
29
 * @see https://github.com/osuushi/Smooth.js
30
 */
31
32
/**
33
 * A class to get scaled values out of arrays.
34
 * @extends WaveFileReader
35
 */
36
export class Interpolator {
37
  
38
  /**
39
   * @param {number} scaleFrom the length of the original array.
40
   * @param {number} scaleTo The length of the new array.
41
   * @param {?Object} details The extra configuration, if needed.
42
   */
43
  constructor(scaleFrom, scaleTo, details) {
44
    /**
45
     * The length of the original array.
46
     * @type {number}
47
     */
48
    this.length_ = scaleFrom;
49
    /**
50
     * The scaling factor.
51
     * @type {number}
52
     */
53
    this.scaleFactor_ = (scaleFrom - 1) / scaleTo;
54
    /**
55
     * The interpolation function.
56
     * @type {Function}
57
     */
58
    this.interpolate = this.sinc;
59
    if (details.method === 'point') {
60
    	this.interpolate = this.point;
61
    } else if(details.method === 'linear') {
62
    	this.interpolate = this.linear;
63
    } else if(details.method === 'cubic') {
64
    	this.interpolate = this.cubic;
65
    }
66
    /**
67
     * The tanget factor for cubic interpolation.
68
     * @type {number}
69
     */
70
    this.tangentFactor_ = 1 - Math.max(0, Math.min(1, details.tension || 0));
71
    // Configure the kernel for sinc
72
    /**
73
     * The sinc filter size.
74
     * @type {number}
75
     */
76
    this.sincFilterSize_ = details.sincFilterSize || 1;
77
    /**
78
     * The sinc kernel.
79
     * @type {Function}
80
     */
81
    this.kernel_ = sincKernel_(details.sincWindow || window_);
82
  }
83
84
  /**
85
   * @param {number} t The index to interpolate.
86
   * @param {Array|TypedArray} samples the original array.
87
   * @return {number} The interpolated value.
88
   */
89
  point(t, samples) {
90
    return this.getClippedInput_(Math.round(this.scaleFactor_ * t), samples);
91
  }
92
93
  /**
94
   * @param {number} t The index to interpolate.
95
   * @param {Array|TypedArray} samples the original array.
96
   * @return {number} The interpolated value.
97
   */
98
  linear(t, samples) {
99
    t = this.scaleFactor_ * t;
100
    let k = Math.floor(t);
101
    t -= k;
102
    return (1 - t) *
103
    	this.getClippedInput_(k, samples) + t *
104
    	this.getClippedInput_(k + 1, samples);
105
  }
106
107
  /**
108
   * @param {number} t The index to interpolate.
109
   * @param {Array|TypedArray} samples the original array.
110
   * @return {number} The interpolated value.
111
   */
112
  cubic(t, samples) {
113
    t = this.scaleFactor_ * t;
114
    let k = Math.floor(t);
115
    let m = [this.getTangent_(k, samples), this.getTangent_(k + 1, samples)];
116
    let p = [this.getClippedInput_(k, samples),
117
      this.getClippedInput_(k + 1, samples)];
118
    t -= k;
119
    let t2 = t * t;
120
    let t3 = t * t2;
121
    return (2 * t3 - 3 * t2 + 1) *
122
      p[0] + (t3 - 2 * t2 + t) *
123
      m[0] + (-2 * t3 + 3 * t2) *
124
      p[1] + (t3 - t2) * m[1];
125
  }
126
127
  /**
128
   * @param {number} t The index to interpolate.
129
   * @param {Array|TypedArray} samples the original array.
130
   * @return {number} The interpolated value.
131
   */
132
  sinc(t, samples) {
133
    t = this.scaleFactor_ * t;
134
    let k = Math.floor(t);
135
    let ref = k - this.sincFilterSize_ + 1;
136
    let ref1 = k + this.sincFilterSize_;
137
    let sum = 0;
138
    for (let n = ref; n <= ref1; n++) {
139
      sum += this.kernel_(t - n) * this.getClippedInput_(n, samples);
140
    }
141
    return sum;
142
  }
143
144
  /**
145
   * @param {number} k The scaled index to interpolate.
146
   * @param {Array|TypedArray} samples the original array.
147
   * @return {number} The tangent.
148
   * @private
149
   */
150
  getTangent_(k, samples) {
151
    return this.tangentFactor_ *
152
      (this.getClippedInput_(k + 1, samples) -
153
        this.getClippedInput_(k - 1, samples)) / 2;
154
  }
155
156
  /**
157
   * @param {number} t The scaled index to interpolate.
158
   * @param {Array|TypedArray} samples the original array.
159
   * @return {number} The interpolated value.
160
   * @private
161
   */
162
  getClippedInput_(t, samples) {
163
    if ((0 <= t && t < this.length_)) {
164
      return samples[t];
165
    }
166
    return 0;
167
  }
168
}
169
170
/**
171
 * The default window function.
172
 * @param {number} x The sinc signal.
173
 * @return {number}
174
 * @private
175
 */
176
function window_(x) {
177
  return Math.exp(-x / 2 * x / 2);
178
}
179
180
/**
181
 * @param {Function} window The window function.
182
 * @return {Function}
183
 * @private
184
 */
185
function sincKernel_(window) {
186
  return function(x) { return sinc_(x) * window(x); };
187
}
188
189
/**
190
 * @param {number} x The sinc signal.
191
 * @return {number}
192
 * @private
193
 */
194
function sinc_(x) {
195
  if (x === 0) {
196
    return 1;
197
  }
198
  return Math.sin(Math.PI * x) / (Math.PI * x);
199
}
200