@thi.ng/dsp 4.6.16 → 4.6.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +1 -1
  3. package/add.js +24 -38
  4. package/addg.js +4 -19
  5. package/adsr.js +148 -156
  6. package/agen.js +17 -21
  7. package/allpass.js +37 -38
  8. package/alt.js +26 -22
  9. package/anti-alias.js +8 -37
  10. package/api.js +0 -1
  11. package/aproc.js +24 -30
  12. package/biquad.js +188 -184
  13. package/bounce.js +15 -16
  14. package/complex.js +4 -1
  15. package/const.js +12 -13
  16. package/convert.js +18 -64
  17. package/cosine.js +43 -48
  18. package/curve.js +12 -41
  19. package/dcblock.js +9 -10
  20. package/delay.js +100 -111
  21. package/feedback-delay.js +23 -30
  22. package/fft.js +188 -334
  23. package/filter-delay.js +23 -27
  24. package/filter-response.js +22 -33
  25. package/foldback.js +26 -35
  26. package/impulse-train.js +35 -37
  27. package/impulse.js +27 -42
  28. package/integrator.js +27 -32
  29. package/internal/ensure.js +4 -1
  30. package/internal/take.js +8 -5
  31. package/iterable.js +32 -43
  32. package/line.js +10 -25
  33. package/madd.js +25 -40
  34. package/mapg.js +77 -69
  35. package/merge.js +18 -20
  36. package/mix.js +20 -16
  37. package/mul.js +24 -38
  38. package/multiplex.js +15 -11
  39. package/onepole.js +41 -42
  40. package/osc-additive.js +25 -53
  41. package/osc-cos.js +4 -1
  42. package/osc-dsf.js +15 -52
  43. package/osc-mix.js +6 -20
  44. package/osc-parabolic.js +4 -13
  45. package/osc-rect.js +6 -8
  46. package/osc-saw.js +4 -1
  47. package/osc-sin.js +4 -1
  48. package/osc-tri.js +4 -1
  49. package/osc-wavetable.js +12 -9
  50. package/osc.js +40 -74
  51. package/package.json +13 -10
  52. package/pan.js +23 -29
  53. package/pink-noise.js +35 -57
  54. package/pipe.js +6 -4
  55. package/power.js +40 -96
  56. package/product.js +5 -4
  57. package/reciprocal.js +20 -22
  58. package/ref.js +24 -20
  59. package/serial.js +59 -60
  60. package/sincos.js +45 -61
  61. package/sum.js +5 -4
  62. package/svf.js +74 -78
  63. package/sweep.js +4 -27
  64. package/waveshaper.js +41 -60
  65. package/white-noise.js +17 -23
  66. package/window.js +67 -52
package/fft.js CHANGED
@@ -2,355 +2,209 @@ import { isComplex } from "./complex.js";
2
2
  import { magDb } from "./convert.js";
3
3
  import { invPowerScale, powerScale } from "./power.js";
4
4
  import { applyWindow } from "./window.js";
5
- /**
6
- * Returns a new tuple of real/img F64 buffers of given size.
7
- *
8
- * @param n -
9
- */
10
- export const complexArray = (n) => [
11
- new Float64Array(n),
12
- new Float64Array(n),
5
+ const complexArray = (n) => [
6
+ new Float64Array(n),
7
+ new Float64Array(n)
13
8
  ];
14
- /**
15
- * Creates a deep copy of given {@link ComplexArray}.
16
- *
17
- * @param complex -
18
- */
19
- export const copyComplex = (complex) => [
20
- complex[0].slice(),
21
- complex[1].slice(),
9
+ const copyComplex = (complex) => [
10
+ complex[0].slice(),
11
+ complex[1].slice()
22
12
  ];
23
- export function conjugate(src, isImg = true) {
24
- if (isComplex(src)) {
25
- const n = src[0].length;
26
- const res = complexArray(n * 2);
27
- const [sreal, simg] = src;
28
- const [dreal, dimg] = res;
29
- dreal.set(sreal);
30
- dimg.set(simg);
31
- for (let i = 1, j = n * 2 - 1; i < n; i++, j--) {
32
- dreal[j] = sreal[i];
33
- dimg[j] = -simg[i];
34
- }
35
- return res;
36
- }
37
- else {
38
- const n = src.length;
39
- const dest = new Float64Array(n * 2);
40
- dest.set(src);
41
- for (let i = 1, j = n * 2 - 1; i < n; i++, j--) {
42
- dest[j] = isImg ? -src[i] : src[i];
43
- }
44
- return dest;
13
+ function conjugate(src, isImg = true) {
14
+ if (isComplex(src)) {
15
+ const n = src[0].length;
16
+ const res = complexArray(n * 2);
17
+ const [sreal, simg] = src;
18
+ const [dreal, dimg] = res;
19
+ dreal.set(sreal);
20
+ dimg.set(simg);
21
+ for (let i = 1, j = n * 2 - 1; i < n; i++, j--) {
22
+ dreal[j] = sreal[i];
23
+ dimg[j] = -simg[i];
45
24
  }
25
+ return res;
26
+ } else {
27
+ const n = src.length;
28
+ const dest = new Float64Array(n * 2);
29
+ dest.set(src);
30
+ for (let i = 1, j = n * 2 - 1; i < n; i++, j--) {
31
+ dest[j] = isImg ? -src[i] : src[i];
32
+ }
33
+ return dest;
34
+ }
46
35
  }
47
36
  const swapR = (real, n) => {
48
- const n2 = n >> 1;
49
- let ii;
50
- let jj;
51
- let k;
52
- let t;
53
- for (let i = 1, j = 1; i < n; i++) {
54
- if (i < j) {
55
- ii = i - 1;
56
- jj = j - 1;
57
- t = real[jj];
58
- real[jj] = real[ii];
59
- real[ii] = t;
60
- }
61
- k = n2;
62
- while (k < j) {
63
- j -= k;
64
- k >>= 1;
65
- }
66
- j += k;
67
- }
37
+ const n2 = n >> 1;
38
+ let ii;
39
+ let jj;
40
+ let k;
41
+ let t;
42
+ for (let i = 1, j = 1; i < n; i++) {
43
+ if (i < j) {
44
+ ii = i - 1;
45
+ jj = j - 1;
46
+ t = real[jj];
47
+ real[jj] = real[ii];
48
+ real[ii] = t;
49
+ }
50
+ k = n2;
51
+ while (k < j) {
52
+ j -= k;
53
+ k >>= 1;
54
+ }
55
+ j += k;
56
+ }
68
57
  };
69
58
  const swapRI = (real, img, n) => {
70
- const n2 = n >> 1;
71
- let ii;
72
- let jj;
73
- let k;
74
- let t;
75
- for (let i = 1, j = 1; i < n; i++) {
76
- if (i < j) {
77
- ii = i - 1;
78
- jj = j - 1;
79
- t = real[jj];
80
- real[jj] = real[ii];
81
- real[ii] = t;
82
- t = img[jj];
83
- img[jj] = img[ii];
84
- img[ii] = t;
85
- }
86
- k = n2;
87
- while (k < j) {
88
- j -= k;
89
- k >>= 1;
90
- }
91
- j += k;
92
- }
59
+ const n2 = n >> 1;
60
+ let ii;
61
+ let jj;
62
+ let k;
63
+ let t;
64
+ for (let i = 1, j = 1; i < n; i++) {
65
+ if (i < j) {
66
+ ii = i - 1;
67
+ jj = j - 1;
68
+ t = real[jj];
69
+ real[jj] = real[ii];
70
+ real[ii] = t;
71
+ t = img[jj];
72
+ img[jj] = img[ii];
73
+ img[ii] = t;
74
+ }
75
+ k = n2;
76
+ while (k < j) {
77
+ j -= k;
78
+ k >>= 1;
79
+ }
80
+ j += k;
81
+ }
93
82
  };
94
83
  const transform = (real, img, n) => {
95
- let step = 1;
96
- let prevStep;
97
- let i, j, ii, ip;
98
- let tr, ti;
99
- let ur, ui;
100
- let wr, wi;
101
- let t;
102
- for (let b = Math.ceil(Math.log2(n)); b-- > 0;) {
103
- prevStep = step;
104
- step <<= 1;
105
- ur = 1;
106
- ui = 0;
107
- t = Math.PI / prevStep;
108
- wr = Math.cos(t);
109
- wi = -Math.sin(t);
110
- for (j = 1; j <= prevStep; j++) {
111
- for (i = j; i <= n; i += step) {
112
- ip = i + prevStep - 1;
113
- ii = i - 1;
114
- tr = real[ip] * ur - img[ip] * ui;
115
- ti = real[ip] * ui + img[ip] * ur;
116
- real[ip] = real[ii] - tr;
117
- img[ip] = img[ii] - ti;
118
- real[ii] += tr;
119
- img[ii] += ti;
120
- }
121
- t = ur;
122
- ur = t * wr - ui * wi;
123
- ui = t * wi + ui * wr;
124
- }
125
- }
84
+ let step = 1;
85
+ let prevStep;
86
+ let i, j, ii, ip;
87
+ let tr, ti;
88
+ let ur, ui;
89
+ let wr, wi;
90
+ let t;
91
+ for (let b = Math.ceil(Math.log2(n)); b-- > 0; ) {
92
+ prevStep = step;
93
+ step <<= 1;
94
+ ur = 1;
95
+ ui = 0;
96
+ t = Math.PI / prevStep;
97
+ wr = Math.cos(t);
98
+ wi = -Math.sin(t);
99
+ for (j = 1; j <= prevStep; j++) {
100
+ for (i = j; i <= n; i += step) {
101
+ ip = i + prevStep - 1;
102
+ ii = i - 1;
103
+ tr = real[ip] * ur - img[ip] * ui;
104
+ ti = real[ip] * ui + img[ip] * ur;
105
+ real[ip] = real[ii] - tr;
106
+ img[ip] = img[ii] - ti;
107
+ real[ii] += tr;
108
+ img[ii] += ti;
109
+ }
110
+ t = ur;
111
+ ur = t * wr - ui * wi;
112
+ ui = t * wi + ui * wr;
113
+ }
114
+ }
126
115
  };
127
- /**
128
- * Computes in-place forward FFT for given real/imaginary component
129
- * buffers (each MUST be pow2 length), optionally with windowing.
130
- *
131
- * @remarks
132
- * If `window` is given, the `real` array will be pre-multiplied with
133
- * the `window`.
134
- *
135
- * No result scaling / normalization is performed. Use
136
- * {@link normalizeFFT} for that purpose.
137
- *
138
- * - https://www.earlevel.com/main/2002/08/31/a-gentle-introduction-to-the-fft/
139
- * - https://www.earlevel.com/main/2019/04/30/waveutils-updated/
140
- * - https://betterexplained.com/articles/an-interactive-guide-to-the-fourier-transform/
141
- * - http://toxi.co.uk/p5/fftDebug/fft4amit.pde
142
- *
143
- * @param complex -
144
- * @param window -
145
- */
146
- export const fft = (complex, window) => {
147
- let real, img;
148
- if (isComplex(complex)) {
149
- real = complex[0];
150
- img = complex[1];
151
- }
152
- else {
153
- real = complex;
154
- }
155
- if (window) {
156
- applyWindow(real, window);
157
- }
158
- const n = real.length;
159
- if (img) {
160
- swapRI(real, img, n);
161
- }
162
- else {
163
- swapR(real, n);
164
- img = new Float64Array(n);
165
- }
166
- transform(real, img, n);
167
- return [real, img];
116
+ const fft = (complex, window) => {
117
+ let real, img;
118
+ if (isComplex(complex)) {
119
+ real = complex[0];
120
+ img = complex[1];
121
+ } else {
122
+ real = complex;
123
+ }
124
+ if (window) {
125
+ applyWindow(real, window);
126
+ }
127
+ const n = real.length;
128
+ if (img) {
129
+ swapRI(real, img, n);
130
+ } else {
131
+ swapR(real, n);
132
+ img = new Float64Array(n);
133
+ }
134
+ transform(real, img, n);
135
+ return [real, img];
168
136
  };
169
- /**
170
- * Inverse FFT via computing forward transform with swapped real/imaginary
171
- * components. Expects denormalized inputs (i.e. the same as the result of
172
- * {@link fft}).
173
- *
174
- * @remarks
175
- *
176
- * - https://www.dsprelated.com/showarticle/800.php (method #3)
177
- *
178
- * @param complex -
179
- */
180
- export const ifft = (src) => {
181
- let complex = isComplex(src)
182
- ? src
183
- : [new Float64Array(src.length), src];
184
- fft([complex[1], complex[0]]);
185
- return scaleFFT(complex, 1 / complex[0].length);
137
+ const ifft = (src) => {
138
+ let complex = isComplex(src) ? src : [new Float64Array(src.length), src];
139
+ fft([complex[1], complex[0]]);
140
+ return scaleFFT(complex, 1 / complex[0].length);
186
141
  };
187
- export const scaleFFT = (complex, scale) => {
188
- const [real, img] = complex;
189
- const n = real.length;
190
- for (let i = 0; i < n; i++) {
191
- real[i] *= scale;
192
- img[i] *= scale;
193
- }
194
- return [real, img];
142
+ const scaleFFT = (complex, scale) => {
143
+ const [real, img] = complex;
144
+ const n = real.length;
145
+ for (let i = 0; i < n; i++) {
146
+ real[i] *= scale;
147
+ img[i] *= scale;
148
+ }
149
+ return [real, img];
195
150
  };
196
- /**
197
- * Normalizes the complex FFT array by scaling each complex bin value with given
198
- * scale factor (or, if given as array, the scale factor derived from these
199
- * window function samples).
200
- *
201
- * @remarks
202
- * By default assumes a rectangular window and the resulting scale factor of 2 /
203
- * N.
204
- *
205
- * References:
206
- * - https://holometer.fnal.gov/GH_FFT.pdf
207
- *
208
- * @param complex -
209
- * @param window -
210
- */
211
- export const normalizeFFT = (complex, window = 2 / complex[0].length) => scaleFFT(complex, powerScale(window, 2));
212
- /**
213
- * Inverse operation of {@link normalizeFFT}. De-normalizes the complex FFT
214
- * array by scaling each complex bin value with given scale factor (or, if given
215
- * as array, the scale factor derived from these window function samples).
216
- *
217
- * @remarks
218
- * By default assumes a rectangular window and the resulting scale factor of N /
219
- * 2.
220
- *
221
- * References:
222
- * - https://holometer.fnal.gov/GH_FFT.pdf
223
- *
224
- * @param complex -
225
- * @param window -
226
- */
227
- export const denormalizeFFT = (complex, window = complex[0].length / 2) => scaleFFT(complex, invPowerScale(window, 2));
228
- /**
229
- * Computes the magnitude of each FFT bin and if less than given `eps`
230
- * threshold, sets that bin to zero. Returns input FFT array.
231
- *
232
- * @remarks
233
- * It's recommended to apply this function prior computing
234
- * {@link spectrumPhase}. The `eps` value might have to be adjusted and should
235
- * be approx. `max(spectrumMag(fft))/10000`.
236
- *
237
- * References:
238
- * - https://www.gaussianwaves.com/2015/11/interpreting-fft-results-obtaining-magnitude-and-phase-information/
239
- *
240
- * @param complex -
241
- * @param eps -
242
- */
243
- export const thresholdFFT = (complex, eps = 1e-12) => {
244
- const [real, img] = complex;
245
- for (let i = 0, n = real.length; i < n; i++) {
246
- if (Math.hypot(real[i], img[i]) < eps) {
247
- real[i] = img[i] = 0;
248
- }
249
- }
250
- return complex;
151
+ const normalizeFFT = (complex, window = 2 / complex[0].length) => scaleFFT(complex, powerScale(window, 2));
152
+ const denormalizeFFT = (complex, window = complex[0].length / 2) => scaleFFT(complex, invPowerScale(window, 2));
153
+ const thresholdFFT = (complex, eps = 1e-12) => {
154
+ const [real, img] = complex;
155
+ for (let i = 0, n = real.length; i < n; i++) {
156
+ if (Math.hypot(real[i], img[i]) < eps) {
157
+ real[i] = img[i] = 0;
158
+ }
159
+ }
160
+ return complex;
251
161
  };
252
- /**
253
- * Computes magnitude spectrum for given FFT: y(i) = abs(c(i)). By default only
254
- * the first N/2 values are returned.
255
- *
256
- * @param complex - FFT result
257
- * @param n - bin count
258
- * @param out - result array
259
- */
260
- export const spectrumMag = (complex, n = complex[0].length / 2, out = []) => {
261
- const [real, img] = complex;
262
- for (let i = 0; i < n; i++) {
263
- out[i] = Math.hypot(real[i], img[i]);
264
- }
265
- return out;
162
+ const spectrumMag = (complex, n = complex[0].length / 2, out = []) => {
163
+ const [real, img] = complex;
164
+ for (let i = 0; i < n; i++) {
165
+ out[i] = Math.hypot(real[i], img[i]);
166
+ }
167
+ return out;
266
168
  };
267
- /**
268
- * Computes power spectrum (optionally as dBFS) for the given FFT result arrays
269
- * (length = N) and optional `window`. Writes result to `out` or a new array.
270
- *
271
- * @remarks
272
- * If `window` is given (scale factor or array), it will be used as (if number)
273
- * or to compute the scaling factor (if array) for each FFT bin's value. The
274
- * default (`window=1`) is the equivalent to a rectangular window (i.e. a
275
- * no-op). If windowing was used to compute the FFT, the same should be provided
276
- * to this function for correct results.
277
- *
278
- * **IMPORTANT:** If the FFT result has already been normalized using
279
- * {@link normalizeFFT}, the scaling factor (`window` arg) MUST be set 1.0.
280
- *
281
- * By default only the first N/2 values are returned. If `db` is true, the
282
- * spectrum values are converted to dBFS.
283
- *
284
- * - https://holometer.fnal.gov/GH_FFT.pdf
285
- * - https://dsp.stackexchange.com/a/32080
286
- * - https://dsp.stackexchange.com/a/14935
287
- * - https://www.kvraudio.com/forum/viewtopic.php?t=276092
288
- *
289
- * @param complex -
290
- * @param db -
291
- * @param window -
292
- * @param n -
293
- * @param out -
294
- */
295
- export const spectrumPow = (complex, db = false, window = 2 / complex[0].length, n = complex[0].length / 2, out = []) => {
296
- const [real, img] = complex;
297
- const scale = powerScale(window, 2);
298
- for (let i = 0; i < n; i++) {
299
- const p = real[i] ** 2 + img[i] ** 2;
300
- out[i] = db ? magDb(Math.sqrt(p) * scale) : p * scale;
301
- }
302
- return out;
169
+ const spectrumPow = (complex, db = false, window = 2 / complex[0].length, n = complex[0].length / 2, out = []) => {
170
+ const [real, img] = complex;
171
+ const scale = powerScale(window, 2);
172
+ for (let i = 0; i < n; i++) {
173
+ const p = real[i] ** 2 + img[i] ** 2;
174
+ out[i] = db ? magDb(Math.sqrt(p) * scale) : p * scale;
175
+ }
176
+ return out;
303
177
  };
304
- /**
305
- * Computes phase spectrum for given FFT and writes results to `out`. By default
306
- * only the first N/2 values are returned.
307
- *
308
- * @remarks
309
- * Consider applying {@link thresholdFFT} prior to computing the phase spectrum
310
- * to avoid exploding floating point error magnitudes.
311
- *
312
- * @param complex - FFT result
313
- * @param n - bin count
314
- * @param out - result array
315
- */
316
- export const spectrumPhase = (complex, n = complex[0].length / 2, out = []) => {
317
- const [real, img] = complex;
318
- for (let i = 0; i < n; i++) {
319
- out[i] = Math.atan2(img[i], real[i]);
320
- }
321
- return out;
178
+ const spectrumPhase = (complex, n = complex[0].length / 2, out = []) => {
179
+ const [real, img] = complex;
180
+ for (let i = 0; i < n; i++) {
181
+ out[i] = Math.atan2(img[i], real[i]);
182
+ }
183
+ return out;
322
184
  };
323
- /**
324
- * Returns FFT bin index for given frequency, sample rate and window
325
- * size. See {@link binFreq} for reverse op.
326
- *
327
- * @param f - frequency
328
- * @param fs - sample rate
329
- * @param n - window size
330
- */
331
- export const freqBin = (f, fs, n) => ((f * n) / fs) | 0;
332
- /**
333
- * Returns frequency for given FFT bin index, sample rate and window
334
- * size. See {@link freqBin} for reverse op.
335
- *
336
- * @param bin - bin
337
- * @param fs - sample rate
338
- * @param n - window size
339
- */
340
- export const binFreq = (bin, fs, n) => (bin * fs) / n;
341
- /**
342
- * Returns array of bin center frequencies for given FFT window size and sample
343
- * rate. By default only the first N/2+1 values are returned (`m` and including
344
- * 0Hz).
345
- *
346
- * @param n - window size
347
- * @param fs - sample rate
348
- * @param m - number of result values
349
- */
350
- export const fftFreq = (n, fs, m = n / 2) => {
351
- const res = new Float64Array(m);
352
- for (let i = 0; i <= m; i++) {
353
- res[i] = binFreq(i, fs, n);
354
- }
355
- return res;
185
+ const freqBin = (f, fs, n) => f * n / fs | 0;
186
+ const binFreq = (bin, fs, n) => bin * fs / n;
187
+ const fftFreq = (n, fs, m = n / 2) => {
188
+ const res = new Float64Array(m);
189
+ for (let i = 0; i <= m; i++) {
190
+ res[i] = binFreq(i, fs, n);
191
+ }
192
+ return res;
193
+ };
194
+ export {
195
+ binFreq,
196
+ complexArray,
197
+ conjugate,
198
+ copyComplex,
199
+ denormalizeFFT,
200
+ fft,
201
+ fftFreq,
202
+ freqBin,
203
+ ifft,
204
+ normalizeFFT,
205
+ scaleFFT,
206
+ spectrumMag,
207
+ spectrumPhase,
208
+ spectrumPow,
209
+ thresholdFFT
356
210
  };
package/filter-delay.js CHANGED
@@ -1,30 +1,26 @@
1
1
  import { clamp01 } from "@thi.ng/math/interval";
2
2
  import { Delay } from "./delay.js";
3
- /**
4
- * Extension of {@link feedbackDelay} with an additional filter/proc applied to
5
- * the feedback itself (e.g. a low pass filter).
6
- *
7
- * @param n - delay length
8
- * @param filter - IProc applied to feedback
9
- * @param feedback - feedback factor (default: 0.5)
10
- */
11
- export const filterFeedbackDelay = (n, filter, feedback) => new FilterFeedbackDelay(n, filter, feedback);
12
- export class FilterFeedbackDelay extends Delay {
13
- filter;
14
- _feedback;
15
- constructor(n, filter, _feedback = 0.5) {
16
- super(n, 0);
17
- this.filter = filter;
18
- this._feedback = _feedback;
19
- this.setFeedback(_feedback);
20
- }
21
- next(x) {
22
- return super.next(x + this.filter.next(this._buf[this._rpos] * this._feedback));
23
- }
24
- feedback() {
25
- return this._feedback;
26
- }
27
- setFeedback(feedback) {
28
- this._feedback = clamp01(feedback);
29
- }
3
+ const filterFeedbackDelay = (n, filter, feedback) => new FilterFeedbackDelay(n, filter, feedback);
4
+ class FilterFeedbackDelay extends Delay {
5
+ constructor(n, filter, _feedback = 0.5) {
6
+ super(n, 0);
7
+ this.filter = filter;
8
+ this._feedback = _feedback;
9
+ this.setFeedback(_feedback);
10
+ }
11
+ next(x) {
12
+ return super.next(
13
+ x + this.filter.next(this._buf[this._rpos] * this._feedback)
14
+ );
15
+ }
16
+ feedback() {
17
+ return this._feedback;
18
+ }
19
+ setFeedback(feedback) {
20
+ this._feedback = clamp01(feedback);
21
+ }
30
22
  }
23
+ export {
24
+ FilterFeedbackDelay,
25
+ filterFeedbackDelay
26
+ };
@@ -2,39 +2,28 @@ import { cossin } from "@thi.ng/math/angle";
2
2
  import { TAU } from "@thi.ng/math/api";
3
3
  import { magDb } from "./convert.js";
4
4
  import { line } from "./line.js";
5
- /**
6
- * Returns filter response for given filter coefficients at normalized
7
- * frequency `f`. If `db` is true (default), the magnitude in the
8
- * returned object will be in dBFS.
9
- *
10
- * References:
11
- *
12
- * - https://www.earlevel.com/main/2016/12/01/evaluating-filter-frequency-response/
13
- * - https://www.earlevel.com/main/2016/12/08/filter-frequency-response-grapher/
14
- * - https://github.com/mohayonao/freqr
15
- *
16
- * @param zeroes -
17
- * @param poles -
18
- * @param freq -
19
- * @param db -
20
- */
21
- export const filterResponseRaw = (zeroes, poles, freq, db = true) => {
22
- const w0 = TAU * freq;
23
- const [cp, sp] = convolve(poles, w0);
24
- const [cz, sz] = convolve(zeroes, w0);
25
- const mag = Math.sqrt((cz * cz + sz * sz) / (cp * cp + sp * sp));
26
- const phase = Math.atan2(sp, cp) - Math.atan2(sz, cz);
27
- return { freq, phase, mag: db ? magDb(mag) : mag };
5
+ const filterResponseRaw = (zeroes, poles, freq, db = true) => {
6
+ const w0 = TAU * freq;
7
+ const [cp, sp] = convolve(poles, w0);
8
+ const [cz, sz] = convolve(zeroes, w0);
9
+ const mag = Math.sqrt((cz * cz + sz * sz) / (cp * cp + sp * sp));
10
+ const phase = Math.atan2(sp, cp) - Math.atan2(sz, cz);
11
+ return { freq, phase, mag: db ? magDb(mag) : mag };
28
12
  };
29
- export const filterResponse = (coeffs, freq, db) => filterResponseRaw(coeffs.zeroes, coeffs.poles, freq, db);
30
- export const freqRange = (fstart, fend, num) => line(fstart, fend, num - 1).take(num);
13
+ const filterResponse = (coeffs, freq, db) => filterResponseRaw(coeffs.zeroes, coeffs.poles, freq, db);
14
+ const freqRange = (fstart, fend, num) => line(fstart, fend, num - 1).take(num);
31
15
  const convolve = (coeffs, w0) => {
32
- let c = 0;
33
- let s = 0;
34
- for (let i = coeffs.length; i-- > 0;) {
35
- const k = cossin(w0 * i, coeffs[i]);
36
- c += k[0];
37
- s += k[1];
38
- }
39
- return [c, s];
16
+ let c = 0;
17
+ let s = 0;
18
+ for (let i = coeffs.length; i-- > 0; ) {
19
+ const k = cossin(w0 * i, coeffs[i]);
20
+ c += k[0];
21
+ s += k[1];
22
+ }
23
+ return [c, s];
24
+ };
25
+ export {
26
+ filterResponse,
27
+ filterResponseRaw,
28
+ freqRange
40
29
  };