react-native-audio-api 0.4.11 → 0.4.12-beta.1

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 (55) hide show
  1. package/common/cpp/HostObjects/AudioBufferSourceNodeHostObject.h +1 -2
  2. package/common/cpp/HostObjects/BaseAudioContextHostObject.h +8 -0
  3. package/common/cpp/HostObjects/GainNodeHostObject.h +2 -2
  4. package/common/cpp/HostObjects/StretcherNodeHostObject.h +35 -0
  5. package/common/cpp/core/AudioBus.cpp +8 -0
  6. package/common/cpp/core/AudioBus.h +3 -0
  7. package/common/cpp/core/AudioDestinationNode.cpp +1 -1
  8. package/common/cpp/core/AudioNode.cpp +9 -5
  9. package/common/cpp/core/AudioNode.h +4 -2
  10. package/common/cpp/core/BaseAudioContext.cpp +7 -0
  11. package/common/cpp/core/BaseAudioContext.h +2 -0
  12. package/common/cpp/core/StretcherNode.cpp +96 -0
  13. package/common/cpp/core/StretcherNode.h +63 -0
  14. package/common/cpp/installer/AudioAPIModuleInstaller.h +4 -4
  15. package/common/cpp/libs/dsp/LICENSE.txt +21 -0
  16. package/common/cpp/libs/dsp/README.md +40 -0
  17. package/common/cpp/libs/dsp/common.h +47 -0
  18. package/common/cpp/libs/dsp/curves.h +371 -0
  19. package/common/cpp/libs/dsp/delay.h +717 -0
  20. package/common/cpp/libs/dsp/envelopes.h +523 -0
  21. package/common/cpp/libs/dsp/fft.h +523 -0
  22. package/common/cpp/libs/dsp/filters.h +436 -0
  23. package/common/cpp/libs/dsp/mix.h +218 -0
  24. package/common/cpp/libs/dsp/perf.h +84 -0
  25. package/common/cpp/libs/dsp/rates.h +184 -0
  26. package/common/cpp/libs/dsp/spectral.h +496 -0
  27. package/common/cpp/libs/dsp/windows.h +219 -0
  28. package/common/cpp/libs/signalsmith-stretch.h +637 -0
  29. package/common/cpp/types/TimeStretchType.h +6 -0
  30. package/ios/core/AudioPlayer.m +2 -2
  31. package/lib/module/core/BaseAudioContext.js +4 -0
  32. package/lib/module/core/BaseAudioContext.js.map +1 -1
  33. package/lib/module/core/StretcherNode.js +12 -0
  34. package/lib/module/core/StretcherNode.js.map +1 -0
  35. package/lib/module/index.js +1 -0
  36. package/lib/module/index.js.map +1 -1
  37. package/lib/module/index.web.js +1 -1
  38. package/lib/module/index.web.js.map +1 -1
  39. package/lib/typescript/core/BaseAudioContext.d.ts +2 -0
  40. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  41. package/lib/typescript/core/StretcherNode.d.ts +10 -0
  42. package/lib/typescript/core/StretcherNode.d.ts.map +1 -0
  43. package/lib/typescript/index.d.ts +1 -0
  44. package/lib/typescript/index.d.ts.map +1 -1
  45. package/lib/typescript/index.web.d.ts +1 -1
  46. package/lib/typescript/index.web.d.ts.map +1 -1
  47. package/lib/typescript/interfaces.d.ts +5 -0
  48. package/lib/typescript/interfaces.d.ts.map +1 -1
  49. package/package.json +1 -1
  50. package/src/core/BaseAudioContext.ts +5 -0
  51. package/src/core/StretcherNode.ts +15 -0
  52. package/src/index.ts +1 -0
  53. package/src/index.web.ts +1 -0
  54. package/src/interfaces.ts +6 -0
  55. package/common/cpp/installer/AudioAPIModuleInstaller.cpp +0 -49
@@ -0,0 +1,717 @@
1
+ #include "./common.h"
2
+
3
+ #ifndef SIGNALSMITH_DSP_DELAY_H
4
+ #define SIGNALSMITH_DSP_DELAY_H
5
+
6
+ #include <vector>
7
+ #include <array>
8
+ #include <cmath> // for std::ceil()
9
+ #include <type_traits>
10
+
11
+ #include <complex>
12
+ #include "./fft.h"
13
+ #include "./windows.h"
14
+
15
+ namespace signalsmith {
16
+ namespace delay {
17
+ /** @defgroup Delay Delay utilities
18
+ @brief Standalone templated classes for delays
19
+
20
+ You can set up a `Buffer` or `MultiBuffer`, and get interpolated samples using a `Reader` (separately on each channel in the multi-channel case) - or you can use `Delay`/`MultiDelay` which include their own buffers.
21
+
22
+ Interpolation quality is chosen using a template class, from @ref Interpolators.
23
+
24
+ @{
25
+ @file
26
+ */
27
+
28
+ /** @brief Single-channel delay buffer
29
+
30
+ Access is used with `buffer[]`, relative to the internal read/write position ("head"). This head is moved using `++buffer` (or `buffer += n`), such that `buffer[1] == (buffer + 1)[0]` in a similar way iterators/pointers.
31
+
32
+ Operations like `buffer - 10` or `buffer++` return a View, which holds a fixed position in the buffer (based on the read/write position at the time).
33
+
34
+ The capacity includes both positive and negative indices. For example, a capacity of 100 would support using any of the ranges:
35
+
36
+ * `buffer[-99]` to buffer[0]`
37
+ * `buffer[-50]` to buffer[49]`
38
+ * `buffer[0]` to buffer[99]`
39
+
40
+ Although buffers are usually used with historical samples accessed using negative indices e.g. `buffer[-10]`, you could equally use it flipped around (moving the head backwards through the buffer using `--buffer`).
41
+ */
42
+ template<typename Sample>
43
+ class Buffer {
44
+ unsigned bufferIndex;
45
+ unsigned bufferMask;
46
+ std::vector<Sample> buffer;
47
+ public:
48
+ Buffer(int minCapacity=0) {
49
+ resize(minCapacity);
50
+ }
51
+ // We shouldn't accidentally copy a delay buffer
52
+ Buffer(const Buffer &other) = delete;
53
+ Buffer & operator =(const Buffer &other) = delete;
54
+ // But moving one is fine
55
+ Buffer(Buffer &&other) = default;
56
+ Buffer & operator =(Buffer &&other) = default;
57
+
58
+ void resize(int minCapacity, Sample value=Sample()) {
59
+ int bufferLength = 1;
60
+ while (bufferLength < minCapacity) bufferLength *= 2;
61
+ buffer.assign(bufferLength, value);
62
+ bufferMask = unsigned(bufferLength - 1);
63
+ bufferIndex = 0;
64
+ }
65
+ void reset(Sample value=Sample()) {
66
+ buffer.assign(buffer.size(), value);
67
+ }
68
+
69
+ /// Holds a view for a particular position in the buffer
70
+ template<bool isConst>
71
+ class View {
72
+ using CBuffer = typename std::conditional<isConst, const Buffer, Buffer>::type;
73
+ using CSample = typename std::conditional<isConst, const Sample, Sample>::type;
74
+ CBuffer *buffer = nullptr;
75
+ unsigned bufferIndex = 0;
76
+ public:
77
+ View(CBuffer &buffer, int offset=0) : buffer(&buffer), bufferIndex(buffer.bufferIndex + (unsigned)offset) {}
78
+ View(const View &other, int offset=0) : buffer(other.buffer), bufferIndex(other.bufferIndex + (unsigned)offset) {}
79
+ View & operator =(const View &other) {
80
+ buffer = other.buffer;
81
+ bufferIndex = other.bufferIndex;
82
+ return *this;
83
+ }
84
+
85
+ CSample & operator[](int offset) {
86
+ return buffer->buffer[(bufferIndex + (unsigned)offset)&buffer->bufferMask];
87
+ }
88
+ const Sample & operator[](int offset) const {
89
+ return buffer->buffer[(bufferIndex + (unsigned)offset)&buffer->bufferMask];
90
+ }
91
+
92
+ /// Write data into the buffer
93
+ template<typename Data>
94
+ void write(Data &&data, int length) {
95
+ for (int i = 0; i < length; ++i) {
96
+ (*this)[i] = data[i];
97
+ }
98
+ }
99
+ /// Read data out from the buffer
100
+ template<typename Data>
101
+ void read(int length, Data &&data) const {
102
+ for (int i = 0; i < length; ++i) {
103
+ data[i] = (*this)[i];
104
+ }
105
+ }
106
+
107
+ View operator +(int offset) const {
108
+ return View(*this, offset);
109
+ }
110
+ View operator -(int offset) const {
111
+ return View(*this, -offset);
112
+ }
113
+ };
114
+ using MutableView = View<false>;
115
+ using ConstView = View<true>;
116
+
117
+ MutableView view(int offset=0) {
118
+ return MutableView(*this, offset);
119
+ }
120
+ ConstView view(int offset=0) const {
121
+ return ConstView(*this, offset);
122
+ }
123
+ ConstView constView(int offset=0) const {
124
+ return ConstView(*this, offset);
125
+ }
126
+
127
+ Sample & operator[](int offset) {
128
+ return buffer[(bufferIndex + (unsigned)offset)&bufferMask];
129
+ }
130
+ const Sample & operator[](int offset) const {
131
+ return buffer[(bufferIndex + (unsigned)offset)&bufferMask];
132
+ }
133
+
134
+ /// Write data into the buffer
135
+ template<typename Data>
136
+ void write(Data &&data, int length) {
137
+ for (int i = 0; i < length; ++i) {
138
+ (*this)[i] = data[i];
139
+ }
140
+ }
141
+ /// Read data out from the buffer
142
+ template<typename Data>
143
+ void read(int length, Data &&data) const {
144
+ for (int i = 0; i < length; ++i) {
145
+ data[i] = (*this)[i];
146
+ }
147
+ }
148
+
149
+ Buffer & operator ++() {
150
+ ++bufferIndex;
151
+ return *this;
152
+ }
153
+ Buffer & operator +=(int i) {
154
+ bufferIndex += (unsigned)i;
155
+ return *this;
156
+ }
157
+ Buffer & operator --() {
158
+ --bufferIndex;
159
+ return *this;
160
+ }
161
+ Buffer & operator -=(int i) {
162
+ bufferIndex -= (unsigned)i;
163
+ return *this;
164
+ }
165
+
166
+ MutableView operator ++(int) {
167
+ MutableView view(*this);
168
+ ++bufferIndex;
169
+ return view;
170
+ }
171
+ MutableView operator +(int i) {
172
+ return MutableView(*this, i);
173
+ }
174
+ ConstView operator +(int i) const {
175
+ return ConstView(*this, i);
176
+ }
177
+ MutableView operator --(int) {
178
+ MutableView view(*this);
179
+ --bufferIndex;
180
+ return view;
181
+ }
182
+ MutableView operator -(int i) {
183
+ return MutableView(*this, -i);
184
+ }
185
+ ConstView operator -(int i) const {
186
+ return ConstView(*this, -i);
187
+ }
188
+ };
189
+
190
+ /** @brief Multi-channel delay buffer
191
+
192
+ This behaves similarly to the single-channel `Buffer`, with the following differences:
193
+
194
+ * `buffer[c]` returns a view for a single channel, which behaves like the single-channel `Buffer::View`.
195
+ * The constructor and `.resize()` take an additional first `channel` argument.
196
+ */
197
+ template<typename Sample>
198
+ class MultiBuffer {
199
+ int channels, stride;
200
+ Buffer<Sample> buffer;
201
+ public:
202
+ using ConstChannel = typename Buffer<Sample>::ConstView;
203
+ using MutableChannel = typename Buffer<Sample>::MutableView;
204
+
205
+ MultiBuffer(int channels=0, int capacity=0) : channels(channels), stride(capacity), buffer(channels*capacity) {}
206
+
207
+ void resize(int nChannels, int capacity, Sample value=Sample()) {
208
+ channels = nChannels;
209
+ stride = capacity;
210
+ buffer.resize(channels*capacity, value);
211
+ }
212
+ void reset(Sample value=Sample()) {
213
+ buffer.reset(value);
214
+ }
215
+
216
+ /// A reference-like multi-channel result for a particular sample index
217
+ template<bool isConst>
218
+ class Stride {
219
+ using CChannel = typename std::conditional<isConst, ConstChannel, MutableChannel>::type;
220
+ using CSample = typename std::conditional<isConst, const Sample, Sample>::type;
221
+ CChannel view;
222
+ int channels, stride;
223
+ public:
224
+ Stride(CChannel view, int channels, int stride) : view(view), channels(channels), stride(stride) {}
225
+ Stride(const Stride &other) : view(other.view), channels(other.channels), stride(other.stride) {}
226
+
227
+ CSample & operator[](int channel) {
228
+ return view[channel*stride];
229
+ }
230
+ const Sample & operator[](int channel) const {
231
+ return view[channel*stride];
232
+ }
233
+
234
+ /// Reads from the buffer into a multi-channel result
235
+ template<class Data>
236
+ void get(Data &&result) const {
237
+ for (int c = 0; c < channels; ++c) {
238
+ result[c] = view[c*stride];
239
+ }
240
+ }
241
+ /// Writes from multi-channel data into the buffer
242
+ template<class Data>
243
+ void set(Data &&data) {
244
+ for (int c = 0; c < channels; ++c) {
245
+ view[c*stride] = data[c];
246
+ }
247
+ }
248
+ template<class Data>
249
+ Stride & operator =(const Data &data) {
250
+ set(data);
251
+ return *this;
252
+ }
253
+ Stride & operator =(const Stride &data) {
254
+ set(data);
255
+ return *this;
256
+ }
257
+ };
258
+
259
+ Stride<false> at(int offset) {
260
+ return {buffer.view(offset), channels, stride};
261
+ }
262
+ Stride<true> at(int offset) const {
263
+ return {buffer.view(offset), channels, stride};
264
+ }
265
+
266
+ /// Holds a particular position in the buffer
267
+ template<bool isConst>
268
+ class View {
269
+ using CChannel = typename std::conditional<isConst, ConstChannel, MutableChannel>::type;
270
+ CChannel view;
271
+ int channels, stride;
272
+ public:
273
+ View(CChannel view, int channels, int stride) : view(view), channels(channels), stride(stride) {}
274
+
275
+ CChannel operator[](int channel) {
276
+ return view + channel*stride;
277
+ }
278
+ ConstChannel operator[](int channel) const {
279
+ return view + channel*stride;
280
+ }
281
+
282
+ Stride<isConst> at(int offset) {
283
+ return {view + offset, channels, stride};
284
+ }
285
+ Stride<true> at(int offset) const {
286
+ return {view + offset, channels, stride};
287
+ }
288
+ };
289
+ using MutableView = View<false>;
290
+ using ConstView = View<true>;
291
+
292
+ MutableView view(int offset=0) {
293
+ return MutableView(buffer.view(offset), channels, stride);
294
+ }
295
+ ConstView view(int offset=0) const {
296
+ return ConstView(buffer.view(offset), channels, stride);
297
+ }
298
+ ConstView constView(int offset=0) const {
299
+ return ConstView(buffer.view(offset), channels, stride);
300
+ }
301
+
302
+ MutableChannel operator[](int channel) {
303
+ return buffer + channel*stride;
304
+ }
305
+ ConstChannel operator[](int channel) const {
306
+ return buffer + channel*stride;
307
+ }
308
+
309
+ MultiBuffer & operator ++() {
310
+ ++buffer;
311
+ return *this;
312
+ }
313
+ MultiBuffer & operator +=(int i) {
314
+ buffer += i;
315
+ return *this;
316
+ }
317
+ MutableView operator ++(int) {
318
+ return MutableView(buffer++, channels, stride);
319
+ }
320
+ MutableView operator +(int i) {
321
+ return MutableView(buffer + i, channels, stride);
322
+ }
323
+ ConstView operator +(int i) const {
324
+ return ConstView(buffer + i, channels, stride);
325
+ }
326
+ MultiBuffer & operator --() {
327
+ --buffer;
328
+ return *this;
329
+ }
330
+ MultiBuffer & operator -=(int i) {
331
+ buffer -= i;
332
+ return *this;
333
+ }
334
+ MutableView operator --(int) {
335
+ return MutableView(buffer--, channels, stride);
336
+ }
337
+ MutableView operator -(int i) {
338
+ return MutableView(buffer - i, channels, stride);
339
+ }
340
+ ConstView operator -(int i) const {
341
+ return ConstView(buffer - i, channels, stride);
342
+ }
343
+ };
344
+
345
+ /** \defgroup Interpolators Interpolators
346
+ \ingroup Delay
347
+ @{ */
348
+ /// Nearest-neighbour interpolator
349
+ /// \diagram{delay-random-access-nearest.svg,aliasing and maximum amplitude/delay errors for different input frequencies}
350
+ template<typename Sample>
351
+ struct InterpolatorNearest {
352
+ static constexpr int inputLength = 1;
353
+ static constexpr Sample latency = -0.5; // Because we're truncating, which rounds down too often
354
+
355
+ template<class Data>
356
+ static Sample fractional(const Data &data, Sample) {
357
+ return data[0];
358
+ }
359
+ };
360
+ /// Linear interpolator
361
+ /// \diagram{delay-random-access-linear.svg,aliasing and maximum amplitude/delay errors for different input frequencies}
362
+ template<typename Sample>
363
+ struct InterpolatorLinear {
364
+ static constexpr int inputLength = 2;
365
+ static constexpr int latency = 0;
366
+
367
+ template<class Data>
368
+ static Sample fractional(const Data &data, Sample fractional) {
369
+ Sample a = data[0], b = data[1];
370
+ return a + fractional*(b - a);
371
+ }
372
+ };
373
+ /// Spline cubic interpolator
374
+ /// \diagram{delay-random-access-cubic.svg,aliasing and maximum amplitude/delay errors for different input frequencies}
375
+ template<typename Sample>
376
+ struct InterpolatorCubic {
377
+ static constexpr int inputLength = 4;
378
+ static constexpr int latency = 1;
379
+
380
+ template<class Data>
381
+ static Sample fractional(const Data &data, Sample fractional) {
382
+ // Cubic interpolation
383
+ Sample a = data[0], b = data[1], c = data[2], d = data[3];
384
+ Sample cbDiff = c - b;
385
+ Sample k1 = (c - a)*0.5;
386
+ Sample k3 = k1 + (d - b)*0.5 - cbDiff*2;
387
+ Sample k2 = cbDiff - k3 - k1;
388
+ return b + fractional*(k1 + fractional*(k2 + fractional*k3)); // 16 ops total, not including the indexing
389
+ }
390
+ };
391
+
392
+ // Efficient Algorithms and Structures for Fractional Delay Filtering Based on Lagrange Interpolation
393
+ // Franck 2009 https://www.aes.org/e-lib/browse.cfm?elib=14647
394
+ namespace _franck_impl {
395
+ template<typename Sample, int n, int low, int high>
396
+ struct ProductRange {
397
+ using Array = std::array<Sample, (n + 1)>;
398
+ static constexpr int mid = (low + high)/2;
399
+ using Left = ProductRange<Sample, n, low, mid>;
400
+ using Right = ProductRange<Sample, n, mid + 1, high>;
401
+
402
+ Left left;
403
+ Right right;
404
+
405
+ const Sample total;
406
+ ProductRange(Sample x) : left(x), right(x), total(left.total*right.total) {}
407
+
408
+ template<class Data>
409
+ Sample calculateResult(Sample extraFactor, const Data &data, const Array &invFactors) {
410
+ return left.calculateResult(extraFactor*right.total, data, invFactors)
411
+ + right.calculateResult(extraFactor*left.total, data, invFactors);
412
+ }
413
+ };
414
+ template<typename Sample, int n, int index>
415
+ struct ProductRange<Sample, n, index, index> {
416
+ using Array = std::array<Sample, (n + 1)>;
417
+
418
+ const Sample total;
419
+ ProductRange(Sample x) : total(x - index) {}
420
+
421
+ template<class Data>
422
+ Sample calculateResult(Sample extraFactor, const Data &data, const Array &invFactors) {
423
+ return extraFactor*data[index]*invFactors[index];
424
+ }
425
+ };
426
+ }
427
+ /** Fixed-order Lagrange interpolation.
428
+ \diagram{interpolator-LagrangeN.svg,aliasing and amplitude/delay errors for different sizes}
429
+ */
430
+ template<typename Sample, int n>
431
+ struct InterpolatorLagrangeN {
432
+ static constexpr int inputLength = n + 1;
433
+ static constexpr int latency = (n - 1)/2;
434
+
435
+ using Array = std::array<Sample, (n + 1)>;
436
+ Array invDivisors;
437
+
438
+ InterpolatorLagrangeN() {
439
+ for (int j = 0; j <= n; ++j) {
440
+ double divisor = 1;
441
+ for (int k = 0; k < j; ++k) divisor *= (j - k);
442
+ for (int k = j + 1; k <= n; ++k) divisor *= (j - k);
443
+ invDivisors[j] = 1/divisor;
444
+ }
445
+ }
446
+
447
+ template<class Data>
448
+ Sample fractional(const Data &data, Sample fractional) const {
449
+ constexpr int mid = n/2;
450
+ using Left = _franck_impl::ProductRange<Sample, n, 0, mid>;
451
+ using Right = _franck_impl::ProductRange<Sample, n, mid + 1, n>;
452
+
453
+ Sample x = fractional + latency;
454
+
455
+ Left left(x);
456
+ Right right(x);
457
+
458
+ return left.calculateResult(right.total, data, invDivisors) + right.calculateResult(left.total, data, invDivisors);
459
+ }
460
+ };
461
+ template<typename Sample>
462
+ using InterpolatorLagrange3 = InterpolatorLagrangeN<Sample, 3>;
463
+ template<typename Sample>
464
+ using InterpolatorLagrange7 = InterpolatorLagrangeN<Sample, 7>;
465
+ template<typename Sample>
466
+ using InterpolatorLagrange19 = InterpolatorLagrangeN<Sample, 19>;
467
+
468
+ /** Fixed-size Kaiser-windowed sinc interpolation.
469
+ \diagram{interpolator-KaiserSincN.svg,aliasing and amplitude/delay errors for different sizes}
470
+ If `minimumPhase` is enabled, a minimum-phase version of the kernel is used:
471
+ \diagram{interpolator-KaiserSincN-min.svg,aliasing and amplitude/delay errors for minimum-phase mode}
472
+ */
473
+ template<typename Sample, int n, bool minimumPhase=false>
474
+ struct InterpolatorKaiserSincN {
475
+ static constexpr int inputLength = n;
476
+ static constexpr Sample latency = minimumPhase ? 0 : (n*Sample(0.5) - 1);
477
+
478
+ int subSampleSteps;
479
+ std::vector<Sample> coefficients;
480
+
481
+ InterpolatorKaiserSincN() : InterpolatorKaiserSincN(0.5 - 0.45/std::sqrt(n)) {}
482
+ InterpolatorKaiserSincN(double passFreq) : InterpolatorKaiserSincN(passFreq, 1 - passFreq) {}
483
+ InterpolatorKaiserSincN(double passFreq, double stopFreq) {
484
+ subSampleSteps = 2*n; // Heuristic again. Really it depends on the bandwidth as well.
485
+ double kaiserBandwidth = (stopFreq - passFreq)*(n + 1.0/subSampleSteps);
486
+ kaiserBandwidth += 1.25/kaiserBandwidth; // We want to place the first zero, but (because using this to window a sinc essentially integrates it in the freq-domain), our ripples (and therefore zeroes) are out of phase. This is a heuristic fix.
487
+ double sincScale = M_PI*(passFreq + stopFreq);
488
+
489
+ double centreIndex = n*subSampleSteps*0.5, scaleFactor = 1.0/subSampleSteps;
490
+ std::vector<Sample> windowedSinc(subSampleSteps*n + 1);
491
+
492
+ ::signalsmith::windows::Kaiser::withBandwidth(kaiserBandwidth, false).fill(windowedSinc, windowedSinc.size());
493
+
494
+ for (size_t i = 0; i < windowedSinc.size(); ++i) {
495
+ double x = (i - centreIndex)*scaleFactor;
496
+ int intX = std::round(x);
497
+ if (intX != 0 && std::abs(x - intX) < 1e-6) {
498
+ // Exact 0s
499
+ windowedSinc[i] = 0;
500
+ } else if (std::abs(x) > 1e-6) {
501
+ double p = x*sincScale;
502
+ windowedSinc[i] *= std::sin(p)/p;
503
+ }
504
+ }
505
+
506
+ if (minimumPhase) {
507
+ signalsmith::fft::FFT<Sample> fft(windowedSinc.size()*2, 1);
508
+ windowedSinc.resize(fft.size(), 0);
509
+ std::vector<std::complex<Sample>> spectrum(fft.size());
510
+ std::vector<std::complex<Sample>> cepstrum(fft.size());
511
+ fft.fft(windowedSinc, spectrum);
512
+ for (size_t i = 0; i < fft.size(); ++i) {
513
+ spectrum[i] = std::log(std::abs(spectrum[i]) + 1e-30);
514
+ }
515
+ fft.fft(spectrum, cepstrum);
516
+ for (size_t i = 1; i < fft.size()/2; ++i) {
517
+ cepstrum[i] *= 0;
518
+ }
519
+ for (size_t i = fft.size()/2 + 1; i < fft.size(); ++i) {
520
+ cepstrum[i] *= 2;
521
+ }
522
+ Sample scaling = Sample(1)/fft.size();
523
+ fft.ifft(cepstrum, spectrum);
524
+
525
+ for (size_t i = 0; i < fft.size(); ++i) {
526
+ Sample phase = spectrum[i].imag()*scaling;
527
+ Sample mag = std::exp(spectrum[i].real()*scaling);
528
+ spectrum[i] = {mag*std::cos(phase), mag*std::sin(phase)};
529
+ }
530
+ fft.ifft(spectrum, cepstrum);
531
+ windowedSinc.resize(subSampleSteps*n + 1);
532
+ windowedSinc.shrink_to_fit();
533
+ for (size_t i = 0; i < windowedSinc.size(); ++i) {
534
+ windowedSinc[i] = cepstrum[i].real()*scaling;
535
+ }
536
+ }
537
+
538
+ // Re-order into FIR fractional-delay blocks
539
+ coefficients.resize(n*(subSampleSteps + 1));
540
+ for (int k = 0; k <= subSampleSteps; ++k) {
541
+ for (int i = 0; i < n; ++i) {
542
+ coefficients[k*n + i] = windowedSinc[(subSampleSteps - k) + i*subSampleSteps];
543
+ }
544
+ }
545
+ }
546
+
547
+ template<class Data>
548
+ Sample fractional(const Data &data, Sample fractional) const {
549
+ Sample subSampleDelay = fractional*subSampleSteps;
550
+ int lowIndex = subSampleDelay;
551
+ if (lowIndex >= subSampleSteps) lowIndex = subSampleSteps - 1;
552
+ Sample subSampleFractional = subSampleDelay - lowIndex;
553
+ int highIndex = lowIndex + 1;
554
+
555
+ Sample sumLow = 0, sumHigh = 0;
556
+ const Sample *coeffLow = coefficients.data() + lowIndex*n;
557
+ const Sample *coeffHigh = coefficients.data() + highIndex*n;
558
+ for (int i = 0; i < n; ++i) {
559
+ sumLow += data[i]*coeffLow[i];
560
+ sumHigh += data[i]*coeffHigh[i];
561
+ }
562
+ return sumLow + (sumHigh - sumLow)*subSampleFractional;
563
+ }
564
+ };
565
+
566
+ template<typename Sample>
567
+ using InterpolatorKaiserSinc20 = InterpolatorKaiserSincN<Sample, 20>;
568
+ template<typename Sample>
569
+ using InterpolatorKaiserSinc8 = InterpolatorKaiserSincN<Sample, 8>;
570
+ template<typename Sample>
571
+ using InterpolatorKaiserSinc4 = InterpolatorKaiserSincN<Sample, 4>;
572
+
573
+ template<typename Sample>
574
+ using InterpolatorKaiserSinc20Min = InterpolatorKaiserSincN<Sample, 20, true>;
575
+ template<typename Sample>
576
+ using InterpolatorKaiserSinc8Min = InterpolatorKaiserSincN<Sample, 8, true>;
577
+ template<typename Sample>
578
+ using InterpolatorKaiserSinc4Min = InterpolatorKaiserSincN<Sample, 4, true>;
579
+ /// @}
580
+
581
+ /** @brief A delay-line reader which uses an external buffer
582
+
583
+ This is useful if you have multiple delay-lines reading from the same buffer.
584
+ */
585
+ template<class Sample, template<typename> class Interpolator=InterpolatorLinear>
586
+ class Reader : public Interpolator<Sample> /* so we can get the empty-base-class optimisation */ {
587
+ using Super = Interpolator<Sample>;
588
+ public:
589
+ Reader () {}
590
+ /// Pass in a configured interpolator
591
+ Reader (const Interpolator<Sample> &interpolator) : Super(interpolator) {}
592
+
593
+ template<typename Buffer>
594
+ Sample read(const Buffer &buffer, Sample delaySamples) const {
595
+ int startIndex = delaySamples;
596
+ Sample remainder = delaySamples - startIndex;
597
+
598
+ // Delay buffers use negative indices, but interpolators use positive ones
599
+ using View = decltype(buffer - startIndex);
600
+ struct Flipped {
601
+ View view;
602
+ Sample operator [](int i) const {
603
+ return view[-i];
604
+ }
605
+ };
606
+ return Super::fractional(Flipped{buffer - startIndex}, remainder);
607
+ }
608
+ };
609
+
610
+ /** @brief A single-channel delay-line containing its own buffer.*/
611
+ template<class Sample, template<typename> class Interpolator=InterpolatorLinear>
612
+ class Delay : private Reader<Sample, Interpolator> {
613
+ using Super = Reader<Sample, Interpolator>;
614
+ Buffer<Sample> buffer;
615
+ public:
616
+ static constexpr Sample latency = Super::latency;
617
+
618
+ Delay(int capacity=0) : buffer(1 + capacity + Super::inputLength) {}
619
+ /// Pass in a configured interpolator
620
+ Delay(const Interpolator<Sample> &interp, int capacity=0) : Super(interp), buffer(1 + capacity + Super::inputLength) {}
621
+
622
+ void reset(Sample value=Sample()) {
623
+ buffer.reset(value);
624
+ }
625
+ void resize(int minCapacity, Sample value=Sample()) {
626
+ buffer.resize(minCapacity + Super::inputLength, value);
627
+ }
628
+
629
+ /** Read a sample from `delaySamples` >= 0 in the past.
630
+ The interpolator may add its own latency on top of this (see `Delay::latency`). The default interpolation (linear) has 0 latency.
631
+ */
632
+ Sample read(Sample delaySamples) const {
633
+ return Super::read(buffer, delaySamples);
634
+ }
635
+ /// Writes a sample. Returns the same object, so that you can say `delay.write(v).read(delay)`.
636
+ Delay & write(Sample value) {
637
+ ++buffer;
638
+ buffer[0] = value;
639
+ return *this;
640
+ }
641
+ };
642
+
643
+ /** @brief A multi-channel delay-line with its own buffer. */
644
+ template<class Sample, template<typename> class Interpolator=InterpolatorLinear>
645
+ class MultiDelay : private Reader<Sample, Interpolator> {
646
+ using Super = Reader<Sample, Interpolator>;
647
+ int channels;
648
+ MultiBuffer<Sample> multiBuffer;
649
+ public:
650
+ static constexpr Sample latency = Super::latency;
651
+
652
+ MultiDelay(int channels=0, int capacity=0) : channels(channels), multiBuffer(channels, 1 + capacity + Super::inputLength) {}
653
+
654
+ void reset(Sample value=Sample()) {
655
+ multiBuffer.reset(value);
656
+ }
657
+ void resize(int nChannels, int capacity, Sample value=Sample()) {
658
+ channels = nChannels;
659
+ multiBuffer.resize(channels, capacity + Super::inputLength, value);
660
+ }
661
+
662
+ /// A single-channel delay-line view, similar to a `const Delay`
663
+ struct ChannelView {
664
+ static constexpr Sample latency = Super::latency;
665
+
666
+ const Super &reader;
667
+ typename MultiBuffer<Sample>::ConstChannel channel;
668
+
669
+ Sample read(Sample delaySamples) const {
670
+ return reader.read(channel, delaySamples);
671
+ }
672
+ };
673
+ ChannelView operator [](int channel) const {
674
+ return ChannelView{*this, multiBuffer[channel]};
675
+ }
676
+
677
+ /// A multi-channel result, lazily calculating samples
678
+ struct DelayView {
679
+ Super &reader;
680
+ typename MultiBuffer<Sample>::ConstView view;
681
+ Sample delaySamples;
682
+
683
+ // Calculate samples on-the-fly
684
+ Sample operator [](int c) const {
685
+ return reader.read(view[c], delaySamples);
686
+ }
687
+ };
688
+ DelayView read(Sample delaySamples) {
689
+ return DelayView{*this, multiBuffer.constView(), delaySamples};
690
+ }
691
+ /// Reads into the provided output structure
692
+ template<class Output>
693
+ void read(Sample delaySamples, Output &output) {
694
+ for (int c = 0; c < channels; ++c) {
695
+ output[c] = Super::read(multiBuffer[c], delaySamples);
696
+ }
697
+ }
698
+ /// Reads separate delays for each channel
699
+ template<class Delays, class Output>
700
+ void readMulti(const Delays &delays, Output &output) {
701
+ for (int c = 0; c < channels; ++c) {
702
+ output[c] = Super::read(multiBuffer[c], delays[c]);
703
+ }
704
+ }
705
+ template<class Data>
706
+ MultiDelay & write(const Data &data) {
707
+ ++multiBuffer;
708
+ for (int c = 0; c < channels; ++c) {
709
+ multiBuffer[c][0] = data[c];
710
+ }
711
+ return *this;
712
+ }
713
+ };
714
+
715
+ /** @} */
716
+ }} // signalsmith::delay::
717
+ #endif // include guard