iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
Oversampler.h
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#pragma once
12
13#define OVERSAMPLING_FACTORS_VA_LIST "None", "2x", "4x", "8x", "16x"
14
15#include <functional>
16#include <cmath>
17
18#include "HIIR/FPUUpsampler2x.h"
19#include "HIIR/FPUDownsampler2x.h"
20
21#include "heapbuf.h"
22#include "ptrlist.h"
23
24#include "IPlugPlatform.h"
25
26BEGIN_IPLUG_NAMESPACE
27
28using namespace hiir;
29
30enum EFactor
31{
32 kNone = 0,
33 k2x,
34 k4x,
35 k8x,
36 k16x,
37 kNumFactors
38};
39
40template<typename T = double>
42{
43public:
44 using BlockProcessFunc = std::function<void(T**, T**, int)>;
45
46 OverSampler(EFactor factor = kNone, bool blockProcessing = true, int nInChannels = 1, int nOutChannels = 1)
47 : mBlockProcessing(blockProcessing)
48 , mNInChannels(nInChannels)
49 , mNOutChannels(nOutChannels)
50 {
51
52 static constexpr double coeffs2x[12] = { 0.036681502163648017, 0.13654762463195794, 0.27463175937945444, 0.42313861743656711, 0.56109869787919531, 0.67754004997416184, 0.76974183386322703, 0.83988962484963892, 0.89226081800387902, 0.9315419599631839, 0.96209454837808417, 0.98781637073289585 };
53 static constexpr double coeffs4x[4] = {0.041893991997656171, 0.16890348243995201, 0.39056077292116603, 0.74389574826847926 };
54 static constexpr double coeffs8x[3] = {0.055748680811302048, 0.24305119574153072, 0.64669913119268196 };
55 static constexpr double coeffs16x[2] = {0.10717745346023573, 0.53091435354504557 };
56
57 for (auto c = 0; c < mNInChannels; c++)
58 {
59 mUpsampler2x.Add(new Upsampler2xFPU<12, T>());
60 mUpsampler4x.Add(new Upsampler2xFPU<4, T>());
61 mUpsampler8x.Add(new Upsampler2xFPU<3, T>());
62 mUpsampler16x.Add(new Upsampler2xFPU<2, T>());
63
64 mUpsampler2x.Get(c)->set_coefs(coeffs2x);
65 mUpsampler4x.Get(c)->set_coefs(coeffs4x);
66 mUpsampler8x.Get(c)->set_coefs(coeffs8x);
67 mUpsampler16x.Get(c)->set_coefs(coeffs16x);
68
69 // ptr location doesn't matter at this stage
70 mNextInputPtrs.Add(mUp2x.Get());
71 }
72
73 for (auto c = 0; c < mNOutChannels; c++)
74 {
75 mDownsampler2x.Add(new Downsampler2xFPU<12, T>());
76 mDownsampler4x.Add(new Downsampler2xFPU<4, T>());
77 mDownsampler8x.Add(new Downsampler2xFPU<3, T>());
78 mDownsampler16x.Add(new Downsampler2xFPU<2, T>());
79
80 mDownsampler2x.Get(c)->set_coefs(coeffs2x);
81 mDownsampler4x.Get(c)->set_coefs(coeffs4x);
82 mDownsampler8x.Get(c)->set_coefs(coeffs8x);
83 mDownsampler16x.Get(c)->set_coefs(coeffs16x);
84
85 // ptr location doesn't matter at this stage
86 mNextOutputPtrs.Add(mDown2x.Get());
87 }
88
89 SetOverSampling(factor);
90
91 Reset();
92 }
93
95 {
96 mUpsampler2x.Empty(true);
97 mDownsampler2x.Empty(true);
98 mUpsampler4x.Empty(true);
99 mDownsampler4x.Empty(true);
100 mUpsampler8x.Empty(true);
101 mDownsampler8x.Empty(true);
102 mUpsampler16x.Empty(true);
103 mDownsampler16x.Empty(true);
104 }
105
106 OverSampler(const OverSampler&) = delete;
107 OverSampler& operator=(const OverSampler&) = delete;
108
109 void Reset(int blockSize = DEFAULT_BLOCK_SIZE)
110 {
111 int numBufSamples = 1;
112
113 if (mBlockProcessing)
114 numBufSamples = blockSize;
115 else
116 {
117 numBufSamples = 1;
118 blockSize = 1;
119 }
120
121 numBufSamples *= mNInChannels;
122
123 mUp2x.Resize(2 * numBufSamples);
124 mUp4x.Resize(4 * numBufSamples);
125 mUp8x.Resize(8 * numBufSamples);
126 mUp16x.Resize(16 * numBufSamples);
127
128 mDown2x.Resize(2 * numBufSamples);
129 mDown4x.Resize(4 * numBufSamples);
130 mDown8x.Resize(8 * numBufSamples);
131 mDown16x.Resize(16 * numBufSamples);
132
133 mUp16BufferPtrs.Empty();
134 mUp8BufferPtrs.Empty();
135 mUp4BufferPtrs.Empty();
136 mUp2BufferPtrs.Empty();
137
138 mDown16BufferPtrs.Empty();
139 mDown8BufferPtrs.Empty();
140 mDown4BufferPtrs.Empty();
141 mDown2BufferPtrs.Empty();
142
143 for (auto c = 0; c < mNInChannels; c++)
144 {
145 mUpsampler2x.Get(c)->clear_buffers();
146 mUpsampler4x.Get(c)->clear_buffers();
147 mUpsampler8x.Get(c)->clear_buffers();
148 mUpsampler16x.Get(c)->clear_buffers();
149
150 mUp2BufferPtrs.Add(mUp2x.Get() + c * 2 * blockSize);
151 mUp4BufferPtrs.Add(mUp4x.Get() + (c * 4 * blockSize));
152 mUp8BufferPtrs.Add(mUp8x.Get() + (c * 8 * blockSize));
153 mUp16BufferPtrs.Add(mUp16x.Get() + (c * 16 * blockSize));
154 }
155
156 for (auto c = 0; c < mNOutChannels; c++)
157 {
158 mDownsampler2x.Get(c)->clear_buffers();
159 mDownsampler4x.Get(c)->clear_buffers();
160 mDownsampler8x.Get(c)->clear_buffers();
161 mDownsampler16x.Get(c)->clear_buffers();
162
163 mDown2BufferPtrs.Add(mDown2x.Get() + c * 2 * blockSize);
164 mDown4BufferPtrs.Add(mDown4x.Get() + (c * 4 * blockSize));
165 mDown8BufferPtrs.Add(mDown8x.Get() + (c * 8 * blockSize));
166 mDown16BufferPtrs.Add(mDown16x.Get() + (c * 16 * blockSize));
167 }
168 }
169
177 void ProcessBlock(T** inputs, T** outputs, int nFrames, int nInChans, int nOutChans, BlockProcessFunc func)
178 {
179 assert(nInChans <= mNInChannels);
180 assert(nOutChans <= mNOutChannels);
181
182 if (mRate != mPrevRate)
183 {
184 switch (mRate) {
185 case 2:
186 mInPtrLoopSrc = &mUp2BufferPtrs;
187 mOutPtrLoopSrc = &mDown2BufferPtrs;
188 break;
189 case 4:
190 mInPtrLoopSrc = &mUp4BufferPtrs;
191 mOutPtrLoopSrc = &mDown4BufferPtrs;
192 break;
193 case 8:
194 mInPtrLoopSrc = &mUp8BufferPtrs;
195 mOutPtrLoopSrc = &mDown8BufferPtrs;
196 break;
197 case 16:
198 mInPtrLoopSrc = &mUp16BufferPtrs;
199 mOutPtrLoopSrc = &mDown16BufferPtrs;
200 break;
201 default:
202 break;
203 }
204
205 mPrevRate = mRate;
206 }
207
208 for (auto c = 0; c < nInChans; c++) {
209 if (mRate >= 2) {
210 mUpsampler2x.Get(c)->process_block(mUp2BufferPtrs.Get(c), inputs[c], nFrames);
211 }
212 if (mRate >= 4) {
213 mUpsampler4x.Get(c)->process_block(mUp4BufferPtrs.Get(c), mUp2BufferPtrs.Get(c), nFrames * 2);
214 }
215 if (mRate >= 8) {
216 mUpsampler8x.Get(c)->process_block(mUp8BufferPtrs.Get(c), mUp4BufferPtrs.Get(c), nFrames * 4);
217 }
218 if (mRate == 16) {
219 mUpsampler16x.Get(c)->process_block(mUp16BufferPtrs.Get(c), mUp8BufferPtrs.Get(c), nFrames * 8);
220 }
221 }
222
223 if (mRate == 1) {
224 func(inputs, outputs, nFrames);
225 } else {
226 for (auto i = 0; i < mRate; i++) {
227 for (auto c = 0; c < nInChans; c++) {
228 mNextInputPtrs.Set(c, mInPtrLoopSrc->Get(c) + (i * nFrames));
229 mNextOutputPtrs.Set(c, mOutPtrLoopSrc->Get(c) + (i * nFrames));
230 }
231 func(mNextInputPtrs.GetList(), mNextOutputPtrs.GetList(), nFrames);
232 }
233 }
234
235 for (auto c = 0; c < nOutChans; c++) {
236 if (mRate == 16) {
237 mDownsampler16x.Get(c)->process_block(mDown8BufferPtrs.Get(c), mDown16BufferPtrs.Get(c), nFrames * 8);
238 }
239 if (mRate >= 8) {
240 mDownsampler8x.Get(c)->process_block(mDown4BufferPtrs.Get(c), mDown8BufferPtrs.Get(c), nFrames * 4);
241 }
242 if (mRate >= 4) {
243 mDownsampler4x.Get(c)->process_block(mDown2BufferPtrs.Get(c), mDown4BufferPtrs.Get(c), nFrames * 2);
244 }
245 if (mRate >= 2) {
246 mDownsampler2x.Get(c)->process_block(outputs[c], mDown2BufferPtrs.Get(c), nFrames);
247 }
248 }
249 }
250
255 T Process(T input, std::function<T(T)> func)
256 {
257 T output;
258
259 if (mRate == 16)
260 {
261 mUpsampler2x.Get(0)->process_sample(mUp2x.Get()[0], mUp2x.Get()[1], input);
262 mUpsampler4x.Get(0)->process_block(mUp4x.Get(), mUp2x.Get(), 2);
263 mUpsampler8x.Get(0)->process_block(mUp8x.Get(), mUp4x.Get(), 4);
264 mUpsampler16x.Get(0)->process_block(mUp16x.Get(), mUp8x.Get(), 8);
265
266 for (auto i = 0; i < 16; i++)
267 {
268 mDown16x.Get()[i] = func(mUp16x.Get()[i]);
269 }
270
271 mDownsampler16x.Get(0)->process_block(mDown8x.Get(), mDown16x.Get(), 8);
272 mDownsampler8x.Get(0)->process_block(mDown4x.Get(), mDown8x.Get(), 4);
273 mDownsampler4x.Get(0)->process_block(mDown2x.Get(), mDown4x.Get(), 2);
274 output = mDownsampler2x.Get(0)->process_sample(mDown2x.Get());
275 }
276 else if (mRate == 8)
277 {
278 mUpsampler2x.Get(0)->process_sample(mUp2x.Get()[0], mUp2x.Get()[1], input);
279 mUpsampler4x.Get(0)->process_block(mUp4x.Get(), mUp2x.Get(), 2);
280 mUpsampler8x.Get(0)->process_block(mUp8x.Get(), mUp4x.Get(), 4);
281
282 for (auto i = 0; i < 8; i++)
283 {
284 mDown8x.Get()[i] = func(mUp8x.Get()[i]);
285 }
286
287 mDownsampler8x.Get(0)->process_block(mDown4x.Get(), mDown8x.Get(), 4);
288 mDownsampler4x.Get(0)->process_block(mDown2x.Get(), mDown4x.Get(), 2);
289 output = mDownsampler2x.Get(0)->process_sample(mDown2x.Get());
290 }
291 else if (mRate == 4)
292 {
293 mUpsampler2x.Get(0)->process_sample(mUp2x.Get()[0], mUp2x.Get()[1], input);
294 mUpsampler4x.Get(0)->process_block(mUp4x.Get(), mUp2x.Get(), 2);
295
296 for (auto i = 0; i < 4; i++)
297 {
298 mDown4x.Get()[i] = func(mUp4x.Get()[i]);
299 }
300
301 mDownsampler4x.Get(0)->process_block(mDown2x.Get(), mDown4x.Get(), 2);
302 output = mDownsampler2x.Get(0)->process_sample(mDown2x.Get());
303 }
304 else if (mRate == 2)
305 {
306 mUpsampler2x.Get(0)->process_sample(mUp2x.Get()[0], mUp2x.Get()[1], input);
307
308 mDown2x.Get()[0] = func(mUp2x.Get()[0]);
309 mDown2x.Get()[1] = func(mUp2x.Get()[1]);
310 output = mDownsampler2x.Get(0)->process_sample(mDown2x.Get());
311 }
312 else
313 {
314 output = func(input);
315 }
316
317 return output;
318 }
319
323 T ProcessGen(std::function<T()> genFunc)
324 {
325 auto ProcessDown16x = [&](T input)
326 {
327 mDown16x.Get()[mWritePos] = (T) input;
328
329 mWritePos++;
330 mWritePos &= 15;
331
332 if (mWritePos == 0)
333 {
334 mDownsampler16x.Get(0)->process_block(mDown8x.Get(), mDown16x.Get(), 8);
335 mDownsampler8x.Get(0)->process_block(mDown4x.Get(), mDown8x.Get(), 4);
336 mDownsampler4x.Get(0)->process_block(mDown2x.Get(), mDown4x.Get(), 2);
337 mDownSamplerOutput = mDownsampler2x.Get(0)->process_sample(mDown2x.Get());
338 }
339 };
340
341 auto ProcessDown8x = [&](T input)
342 {
343 mDown8x.Get()[mWritePos] = (T) input;
344
345 mWritePos++;
346 mWritePos &= 7;
347
348 if (mWritePos == 0)
349 {
350 mDownsampler8x.Get(0)->process_block(mDown4x.Get(), mDown8x.Get(), 4);
351 mDownsampler4x.Get(0)->process_block(mDown2x.Get(), mDown4x.Get(), 2);
352 mDownSamplerOutput = mDownsampler2x.Get(0)->process_sample(mDown2x.Get());
353 }
354 };
355
356 auto ProcessDown4x = [&](T input)
357 {
358 mDown4x.Get()[mWritePos] = (T) input;
359
360 mWritePos++;
361 mWritePos &= 3;
362
363 if (mWritePos == 0)
364 {
365 mDownsampler4x.Get(0)->process_block(mDown2x.Get(), mDown4x.Get(), 2);
366 mDownSamplerOutput = mDownsampler2x.Get(0)->process_sample(mDown2x.Get());
367 }
368 };
369
370 auto ProcessDown2x = [&](T input)
371 {
372 mDown2x.Get()[mWritePos] = (T) input;
373
374 mWritePos = !mWritePos;
375
376 if (mWritePos == 0)
377 {
378 mDownSamplerOutput = mDownsampler2x.Get(0)->process_sample(mDown2x.Get());
379 }
380 };
381
382 T output;
383
384 for (int j = 0; j < mRate; j++)
385 {
386 output = genFunc();
387
388 switch(mRate)
389 {
390 case 2: ProcessDown2x(output); break;
391 case 4: ProcessDown4x(output); break;
392 case 8: ProcessDown8x(output); break;
393 case 16: ProcessDown16x(output); break;
394 default: break;
395 }
396 }
397
398 if (mRate > 1)
399 output = mDownSamplerOutput;
400
401 return output;
402 }
403
404 void SetOverSampling(EFactor factor)
405 {
406 if (factor != mFactor)
407 {
408 mFactor = factor;
409 mRate = std::pow(2, (int) factor);
410
411 Reset();
412 }
413 }
414
415 static EFactor RateToFactor(int rate)
416 {
417 switch (rate)
418 {
419 case 1: return EFactor::kNone;
420 case 2: return EFactor::k2x;
421 case 4: return EFactor::k4x;
422 case 8: return EFactor::k8x;
423 case 16: return EFactor::k16x;
424 default: assert(0); return EFactor::kNone;
425 }
426 }
427
428 int GetRate() const
429 {
430 return mRate;
431 }
432
433private:
434 EFactor mFactor = kNone;
435 int mPrevRate = 0;
436 int mRate = 1;
437 int mWritePos = 0;
438 T mDownSamplerOutput = 0.;
439 bool mBlockProcessing; // false
440 int mNInChannels; // 1
441 int mNOutChannels;
442
443 // the actual data
444 WDL_TypedBuf<T> mUp16x;
445 WDL_TypedBuf<T> mUp8x;
446 WDL_TypedBuf<T> mUp4x;
447 WDL_TypedBuf<T> mUp2x;
448
449 WDL_TypedBuf<T> mDown16x;
450 WDL_TypedBuf<T> mDown8x;
451 WDL_TypedBuf<T> mDown4x;
452 WDL_TypedBuf<T> mDown2x;
453
454 //Ptrs into buffer data
455 WDL_PtrList<T> mUp16BufferPtrs;
456 WDL_PtrList<T> mUp8BufferPtrs;
457 WDL_PtrList<T> mUp4BufferPtrs;
458 WDL_PtrList<T> mUp2BufferPtrs;
459
460 WDL_PtrList<T> mDown16BufferPtrs;
461 WDL_PtrList<T> mDown8BufferPtrs;
462 WDL_PtrList<T> mDown4BufferPtrs;
463 WDL_PtrList<T> mDown2BufferPtrs;
464
465 WDL_PtrList<T> mNextInputPtrs;
466 WDL_PtrList<T> mNextOutputPtrs;
467
468 //Ptrs to the buffer data ptrs, changed depending on rate (block processing only)
469 WDL_PtrList<T>* mInPtrLoopSrc = nullptr;
470 WDL_PtrList<T>* mOutPtrLoopSrc = nullptr;
471
472 //Ptrs to oversamplers for each channel
473 WDL_PtrList<Upsampler2xFPU<12, T>> mUpsampler2x; // for 1x to 2x SR
474 WDL_PtrList<Upsampler2xFPU<4, T>> mUpsampler4x; // for 2x to 4x SR
475 WDL_PtrList<Upsampler2xFPU<3, T>> mUpsampler8x; // for 4x to 8x SR
476 WDL_PtrList<Upsampler2xFPU<2, T>> mUpsampler16x; // for 8x to 16x SR
477
478 WDL_PtrList<Downsampler2xFPU<12, T>> mDownsampler2x; // decimator for 2x to 1x SR
479 WDL_PtrList<Downsampler2xFPU<4, T>> mDownsampler4x; // decimator for 4x to 2x SR
480 WDL_PtrList<Downsampler2xFPU<3, T>> mDownsampler8x; // decimator for 8x to 4x SR
481 WDL_PtrList<Downsampler2xFPU<2, T>> mDownsampler16x; // decimator for 16x to 8x SR
482};
483
484END_IPLUG_NAMESPACE
Include to get consistently named preprocessor macros for different platforms and logging functionali...
T ProcessGen(std::function< T()> genFunc)
Over-sample an per-sample synthesis function.
Definition: Oversampler.h:323
T Process(T input, std::function< T(T)> func)
Over sample an input sample with a per-sample function (up-sample input -> process with function -> d...
Definition: Oversampler.h:255
void ProcessBlock(T **inputs, T **outputs, int nFrames, int nInChans, int nOutChans, BlockProcessFunc func)
Over sample an input block with a per-block function (up sample input -> process with function -> dow...
Definition: Oversampler.h:177