1 /* simple resampler with variety of algorithms
2 * based on the code by Christopher Snowhill
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 module iv
.audioresampler
/*is aliced*/;
20 final class Resampler
{
32 int writePos
, writeFilled
;
33 int readPos
, readFilled
;
43 float[BufferSize
*2] bufferIn
;
44 float[BufferSize
+SincWidth
*2-1] bufferOut
;
48 writePos
= SincWidth
-1;
56 xquality
= Quality
.max
;
65 // duplicate resampler object
66 Resampler
dup () const {
67 auto res
= new Resampler();
72 // make this resampler a clone of another resampler
73 void copyFrom (const(Resampler
) rsrc
) {
74 if (rsrc
is null) { clear(); return; }
75 writePos
= rsrc
.writePos
;
76 writeFilled
= rsrc
.writeFilled
;
77 readPos
= rsrc
.readPos
;
78 readFilled
= rsrc
.readFilled
;
80 phaseInc
= rsrc
.phaseInc
;
81 invPhase
= rsrc
.invPhase
;
82 invPhaseInc
= rsrc
.invPhaseInc
;
83 xquality
= rsrc
.xquality
;
84 delayAdded
= rsrc
.delayAdded
;
85 delayRemoved
= rsrc
.delayRemoved
;
86 lastAmp
= rsrc
.lastAmp
;
88 bufferIn
[] = rsrc
.bufferIn
[];
89 bufferOut
[] = rsrc
.bufferOut
[];
92 // reset this resampler
94 writePos
= SincWidth
-1;
101 //memset(bufferIn.ptr, 0, (SincWidth-1)*bufferIn[0].sizeof);
102 //memset(bufferIn.ptr+BufferSize, 0, (SincWidth-1)*bufferIn[0].sizeof);
103 bufferIn
[0..SincWidth
] = 0;
104 bufferIn
[SincWidth
+BufferSize
..SincWidth
+BufferSize
+SincWidth
] = 0;
105 if (xquality
== Quality
.Blep || xquality
== Quality
.Blam
) {
109 //memset(bufferOut.ptr, 0, bufferOut.sizeof);
115 return cast(Quality
)xquality
;
118 void quality (int xquality
) {
119 if (xquality
< Quality
.min
) xquality
= Quality
.min
;
120 else if (xquality
> Quality
.max
) xquality
= Quality
.max
;
121 if (xquality
!= xquality
) {
122 if (xquality
== Quality
.Blep || xquality
== Quality
.Blep ||
123 xquality
== Quality
.Blam || xquality
== Quality
.Blam
)
134 xquality
= cast(ubyte)xquality
;
137 // return number of samples that resampler is able to accept before overflowing
139 return BufferSize
-writeFilled
;
142 // return number of samples that resampler is able to emit now
144 if (readFilled
< 1 && ((xquality
!= Quality
.Blep
&& xquality
!= Quality
.Blam
) || invPhaseInc
)) doFillAndRemoveDelay();
148 // get processed sample out of resampler
151 if (readFilled
< 1 && phaseInc
) doFillAndRemoveDelay();
152 if (readFilled
< 1) return 0;
153 if (xquality
== Quality
.Blep || xquality
== Quality
.Blam
) {
154 smp
= cast(int)(bufferOut
.ptr
[readPos
]+accum
);
156 smp
= cast(int)bufferOut
.ptr
[readPos
];
158 //assert(smp >= short.min && smp <= short.max);
159 if (smp
< short.min
) smp
= short.min
;
160 else if (smp
> short.max
) smp
= short.max
;
161 return cast(short)smp
;
164 // get processed sample out of resampler
165 float sampleFloat () {
166 if (readFilled
< 1 && phaseInc
) doFillAndRemoveDelay();
167 if (readFilled
< 1) return 0;
168 if (xquality
== Quality
.Blep || xquality
== Quality
.Blam
) {
169 return bufferOut
.ptr
[readPos
]+accum
;
171 return bufferOut
.ptr
[readPos
];
175 // you should remove sample after getting it's value
176 // "normal" resampling should decay accum
177 void removeSample (bool decay
=true) {
178 if (readFilled
> 0) {
179 if (xquality
== Quality
.Blep || xquality
== Quality
.Blam
) {
180 accum
+= bufferOut
.ptr
[readPos
];
181 bufferOut
.ptr
[readPos
] = 0;
183 import core
.stdc
.math
: fabs;
184 accum
-= accum
*(1.0f/8192.0f);
185 if (fabs(accum
) < 1e-20f) accum
= 0;
189 readPos
= (readPos
+1)%BufferSize
;
193 // is resampler ready to emiting output samples?
194 // note that buffer can contain unread samples, it is ok
196 return (writeFilled
> minFilled());
199 // set resampling rate (srate/drate)
200 void rate (double aRateFactor
) {
201 phaseInc
= aRateFactor
;
202 aRateFactor
= 1.0/aRateFactor
;
203 invPhaseInc
= aRateFactor
;
207 void writeSample (short s
) {
208 if (delayAdded
< 0) {
210 writeFilled
= inputDelay();
212 if (writeFilled
< BufferSize
) {
215 bufferIn
.ptr
[writePos
] = s32
;
216 bufferIn
.ptr
[writePos
+BufferSize
] = s32
;
218 writePos
= (writePos
+1)%BufferSize
;
223 void writeSampleFixed (int s
, ubyte depth
) {
224 if (delayAdded
< 0) {
226 writeFilled
= inputDelay();
228 if (writeFilled
< BufferSize
) {
230 s32
/= cast(double)(1<<(depth
-1));
231 bufferIn
.ptr
[writePos
] = s32
;
232 bufferIn
.ptr
[writePos
+BufferSize
] = s32
;
234 writePos
= (writePos
+1)%BufferSize
;
238 enum paddingSize
= SincWidth
-1;
240 // ////////////////////////////////////////////////////////////////////// //
287 int doZoh (float** outbuf
, float* outbufend
) {
288 int ccinsize
= writeFilled
;
289 const(float)* inbuf
= bufferIn
.ptr
+BufferSize
+writePos
-writeFilled
;
293 float* ccoutbuf
= *outbuf
;
294 const(float)* ccinbuf
= inbuf
;
295 const(float)* ccinbufend
= ccinbuf
+ccinsize
;
296 float ccPhase
= phase
;
297 float ccPhaseInc
= phaseInc
;
299 import core
.stdc
.math
: fmod
;
300 if (ccoutbuf
>= outbufend
) break;
301 float sample
= *ccinbuf
;
302 *ccoutbuf
++ = sample
;
303 ccPhase
+= ccPhaseInc
;
304 ccinbuf
+= cast(int)ccPhase
;
305 ccPhase
= fmod(ccPhase
, 1.0f);
306 } while (ccinbuf
< ccinbufend
);
309 used
= cast(int)(ccinbuf
-inbuf
);
315 int doBlep (float** outbuf
, float* outbufend
) {
316 int ccinsize
= writeFilled
;
317 const(float)* inbuf
= bufferIn
.ptr
+BufferSize
+writePos
-writeFilled
;
321 float* ccoutbuf
= *outbuf
;
322 const(float)* ccinbuf
= inbuf
;
323 const(float)* ccinbufend
= ccinbuf
+ccinsize
;
324 float ccLastAmp
= lastAmp
;
325 float ccInvPhase
= invPhase
;
326 float ccInvPhaseInc
= invPhaseInc
;
327 enum int step
= cast(int)(BlepCutoff
*Resolution
);
328 enum int winstep
= Resolution
;
330 import core
.stdc
.math
: fmod
;
331 if (ccoutbuf
+SincWidth
*2 > outbufend
) break;
332 float sample
= (*ccinbuf
++)-ccLastAmp
;
334 float[SincWidth
*2] kernel
= void;
335 float kernelSum
= 0.0f;
336 int phaseReduced
= cast(int)(ccInvPhase
*Resolution
);
337 int phaseAdj
= phaseReduced
*step
/Resolution
;
339 for (; i
>= -SincWidth
+1; --i
) {
341 int winpos
= i
*winstep
;
342 kernelSum
+= kernel
.ptr
[i
+SincWidth
-1] = sincLut
[abs(phaseAdj
-pos
)]*windowLut
[abs(phaseReduced
-winpos
)];
346 for (i
= 0; i
< SincWidth
*2; ++i
) ccoutbuf
[i
] += sample
*kernel
.ptr
[i
];
348 ccInvPhase
+= ccInvPhaseInc
;
349 ccoutbuf
+= cast(int)ccInvPhase
;
350 ccInvPhase
= fmod(ccInvPhase
, 1.0f);
351 } while (ccinbuf
< ccinbufend
);
352 invPhase
= ccInvPhase
;
355 used
= cast(int)(ccinbuf
-inbuf
);
361 int doLinear (float** outbuf
, float* outbufend
) {
362 int ccinsize
= writeFilled
;
363 const(float)* inbuf
= bufferIn
.ptr
+BufferSize
+writePos
-writeFilled
;
367 float* ccoutbuf
= *outbuf
;
368 const(float)* ccinbuf
= inbuf
;
369 const(float)* ccinbufend
= ccinbuf
+ccinsize
;
370 float ccPhase
= phase
;
371 float ccPhaseInc
= phaseInc
;
373 import core
.stdc
.math
: fmod
;
374 if (ccoutbuf
>= outbufend
) break;
375 float sample
= ccinbuf
[0]+(ccinbuf
[1]-ccinbuf
[0])*ccPhase
;
376 *ccoutbuf
++ = sample
;
377 ccPhase
+= ccPhaseInc
;
378 ccinbuf
+= cast(int)ccPhase
;
379 ccPhase
= fmod(ccPhase
, 1.0f);
380 } while (ccinbuf
< ccinbufend
);
383 used
= cast(int)(ccinbuf
-inbuf
);
389 int doBlam (float** outbuf
, float* outbufend
) {
390 int ccinsize
= writeFilled
;
391 const(float)*inbuf
= bufferIn
.ptr
+BufferSize
+writePos
-writeFilled
;
395 float* ccoutbuf
= *outbuf
;
396 const(float)* ccinbuf
= inbuf
;
397 const(float)* ccinbufend
= ccinbuf
+ccinsize
;
398 float ccLastAmp
= lastAmp
;
399 float ccPhase
= phase
;
400 float ccPhaseInc
= phaseInc
;
401 float ccInvPhase
= invPhase
;
402 float ccInvPhaseInc
= invPhaseInc
;
403 enum int step
= cast(int)(BlamCutoff
*Resolution
);
404 enum int winstep
= Resolution
;
406 import core
.stdc
.math
: fmod
;
407 if (ccoutbuf
+SincWidth
*2 > outbufend
) break;
408 float sample
= ccinbuf
[0];
409 if (ccPhaseInc
< 1.0f) sample
+= (ccinbuf
[1]-ccinbuf
[0])*ccPhase
;
412 float[SincWidth
*2] kernel
= void;
413 float kernelSum
= 0.0f;
414 int phaseReduced
= cast(int)(ccInvPhase
*Resolution
);
415 int phaseAdj
= phaseReduced
*step
/Resolution
;
417 for (; i
>= -SincWidth
+1; --i
) {
419 int winpos
= i
*winstep
;
420 kernelSum
+= kernel
.ptr
[i
+SincWidth
-1] = sincLut
[abs(phaseAdj
-pos
)]*windowLut
[abs(phaseReduced
-winpos
)];
424 for (i
= 0; i
< SincWidth
*2; ++i
) ccoutbuf
[i
] += sample
*kernel
.ptr
[i
];
426 if (ccInvPhaseInc
< 1.0f) {
428 ccInvPhase
+= ccInvPhaseInc
;
429 ccoutbuf
+= cast(int)ccInvPhase
;
430 ccInvPhase
= fmod(ccInvPhase
, 1.0f);
432 ccPhase
+= ccPhaseInc
;
434 ccinbuf
+= cast(int)ccPhase
;
435 ccPhase
= fmod(ccPhase
, 1.0f);
437 } while (ccinbuf
< ccinbufend
);
439 invPhase
= ccInvPhase
;
442 used
= cast(int)(ccinbuf
-inbuf
);
448 int doCubic (float** outbuf
, float* outbufend
) {
449 int ccinsize
= writeFilled
;
450 const(float)*inbuf
= bufferIn
.ptr
+BufferSize
+writePos
-writeFilled
;
454 float* ccoutbuf
= *outbuf
;
455 const(float)* ccinbuf
= inbuf
;
456 const(float)* ccinbufend
= ccinbuf
+ccinsize
;
457 float ccPhase
= phase
;
458 float ccPhaseInc
= phaseInc
;
460 import core
.stdc
.math
: fmod
;
463 if (ccoutbuf
>= outbufend
) break;
464 float* kernel
= cubicLut
.ptr
+cast(int)(ccPhase
*Resolution
)*4;
465 for (sample
= 0, i
= 0; i
< 4; ++i
) sample
+= ccinbuf
[i
]*kernel
[i
];
466 *ccoutbuf
++ = sample
;
467 ccPhase
+= ccPhaseInc
;
468 ccinbuf
+= cast(int)ccPhase
;
469 ccPhase
= fmod(ccPhase
, 1.0f);
470 } while (ccinbuf
< ccinbufend
);
473 used
= cast(int)(ccinbuf
-inbuf
);
479 int doSinc (float** outbuf
, float* outbufend
) {
480 int ccinsize
= writeFilled
;
481 const(float)*inbuf
= bufferIn
.ptr
+BufferSize
+writePos
-writeFilled
;
483 ccinsize
-= SincWidth
*2;
485 float* ccoutbuf
= *outbuf
;
486 const(float)* ccinbuf
= inbuf
;
487 const(float)* ccinbufend
= ccinbuf
+ccinsize
;
488 float ccPhase
= phase
;
489 float ccPhaseInc
= phaseInc
;
490 immutable int step
= (ccPhaseInc
> 1.0f ?
cast(int)(Resolution
/ccPhaseInc
*SincCutoff
) : cast(int)(Resolution
*SincCutoff
));
491 enum int winstep
= Resolution
;
493 import core
.stdc
.math
: fmod
;
494 float[SincWidth
*2] kernel
= void;
495 float kernelSum
= 0.0;
497 int phaseReduced
= cast(int)(ccPhase
*Resolution
);
498 int phaseAdj
= phaseReduced
*step
/Resolution
;
500 if (ccoutbuf
>= outbufend
) break;
501 for (; i
>= -SincWidth
+1; --i
) {
503 int winpos
= i
*winstep
;
504 kernelSum
+= kernel
.ptr
[i
+SincWidth
-1] = sincLut
[abs(phaseAdj
-pos
)]*windowLut
[abs(phaseReduced
-winpos
)];
506 for (sample
= 0, i
= 0; i
< SincWidth
*2; ++i
) sample
+= ccinbuf
[i
]*kernel
.ptr
[i
];
507 *ccoutbuf
++ = cast(float)(sample
/kernelSum
);
508 ccPhase
+= ccPhaseInc
;
509 ccinbuf
+= cast(int)ccPhase
;
510 ccPhase
= fmod(ccPhase
, 1.0f);
511 } while (ccinbuf
< ccinbufend
);
514 used
= cast(int)(ccinbuf
-inbuf
);
521 import core
.stdc
.string
: memcpy
;
522 int ccMinFilled
= minFilled();
523 int ccXquality
= xquality
;
524 while (writeFilled
> ccMinFilled
&& readFilled
< BufferSize
) {
525 int ccWritePos
= (readPos
+readFilled
)%BufferSize
;
526 int ccWriteSize
= BufferSize
-ccWritePos
;
527 float* ccoutbuf
= bufferOut
.ptr
+ccWritePos
;
528 if (ccWriteSize
> BufferSize
-readFilled
) ccWriteSize
= BufferSize
-readFilled
;
529 switch (ccXquality
) {
531 doZoh(&ccoutbuf
, ccoutbuf
+ccWriteSize
);
535 int ccWriteExtra
= 0;
536 if (ccWritePos
>= readPos
) ccWriteExtra
= readPos
;
537 if (ccWriteExtra
> SincWidth
*2-1) ccWriteExtra
= SincWidth
*2-1;
538 memcpy(bufferOut
.ptr
+BufferSize
, bufferOut
.ptr
, ccWriteExtra
*bufferOut
[0].sizeof
);
539 used
= doBlep(&ccoutbuf
, ccoutbuf
+ccWriteSize
+ccWriteExtra
);
540 memcpy(bufferOut
.ptr
, bufferOut
.ptr
+BufferSize
, ccWriteExtra
*bufferOut
[0].sizeof
);
544 doLinear(&ccoutbuf
, ccoutbuf
+ccWriteSize
);
547 float* outbuf
= ccoutbuf
;
548 int ccWriteExtra
= 0;
549 if (ccWritePos
>= readPos
) ccWriteExtra
= readPos
;
550 if (ccWriteExtra
> SincWidth
*2-1) ccWriteExtra
= SincWidth
*2-1;
551 memcpy(bufferOut
.ptr
+BufferSize
, bufferOut
.ptr
, ccWriteExtra
*bufferOut
[0].sizeof
);
552 doBlam(&ccoutbuf
, ccoutbuf
+ccWriteSize
+ccWriteExtra
);
553 memcpy(bufferOut
.ptr
, bufferOut
.ptr
+BufferSize
, ccWriteExtra
*bufferOut
[0].sizeof
);
554 if (ccoutbuf
== outbuf
) return;
557 doCubic(&ccoutbuf
, ccoutbuf
+ccWriteSize
);
560 doSinc(&ccoutbuf
, ccoutbuf
+ccWriteSize
);
562 default: assert(0, "wtf?!");
564 readFilled
+= ccoutbuf
-bufferOut
.ptr
-ccWritePos
;
568 void doFillAndRemoveDelay () {
570 if (delayRemoved
< 0) {
571 int delay
= outputDelay();
573 while (delay
--) removeSample(true);
577 // ////////////////////////////////////////////////////////////////////// //
578 enum PI
= 3.14159265358979323846;
581 enum Resolution
= 1<<Shift
;
582 enum ResolutionExtra
= 1<<(Shift
+ShiftExtra
);
584 enum SincSamples
= Resolution
*SincWidth
;
585 enum CubicSamples
= Resolution
*4;
587 enum float BlepCutoff
= 0.90f;
588 enum float BlamCutoff
= 0.93f;
589 enum float SincCutoff
= 0.999f;
591 // ////////////////////////////////////////////////////////////////////// //
593 __gshared
float[CubicSamples
] cubicLut
;
594 __gshared
float[SincSamples
+1] sincLut
;
595 __gshared
float[SincSamples
+1] windowLut
;
597 enum BufferSize
= SincWidth
*4;
599 int abs() (int n
) { pragma(inline
, true); return (n
< 0 ?
-n
: n
); }
600 int fEqual() (const float b
, const float a
) { pragma(inline
, true); import core
.stdc
.math
: fabs; return (fabs(a
-b
) < 1.0e-6f); }
601 float sinc() (float x
) { pragma(inline
, true); import core
.stdc
.math
: sin
; return (fEqual(x
, 0.0) ?
1.0 : sin(x
*PI
)/(x
*PI
)); }
603 shared static this () {
604 import core
.stdc
.math
: fabs, cos
;
605 double dx
= cast(float)(SincWidth
)/SincSamples
, x
= 0.0;
606 for (uint i
= 0; i
< SincSamples
+1; ++i
, x
+= dx
) {
607 float y
= x
/SincWidth
;
608 //float window = 0.42659-0.49656*cos(PI+PI*y)+0.076849*cos(2.0*PI*y); // Blackman
609 float window
= 0.40897+0.5*cos(PI
*y
)+0.09103*cos(2.0*PI
*y
); // Nuttal 3 term
610 //float window = 0.79445*cos(0.5*PI*y)+0.20555*cos(1.5*PI*y); // C.R.Helmrich's 2 term window
611 //float window = sinc(y); // Lanczos
612 sincLut
.ptr
[i
] = fabs(x
) < SincWidth ?
sinc(x
) : 0.0;
613 windowLut
.ptr
[i
] = window
;
615 dx
= 1.0/cast(float)(Resolution
);
617 for (uint i
= 0; i
< Resolution
; ++i
, x
+= dx
) {
618 cubicLut
.ptr
[i
*4] = cast(float)(-0.5*x
*x
*x
+ x
*x
-0.5*x
);
619 cubicLut
.ptr
[i
*4+1] = cast(float)( 1.5*x
*x
*x
-2.5*x
*x
+1.0);
620 cubicLut
.ptr
[i
*4+2] = cast(float)(-1.5*x
*x
*x
+2.0*x
*x
+0.5*x
);
621 cubicLut
.ptr
[i
*4+3] = cast(float)( 0.5*x
*x
*x
-0.5*x
*x
);
628 rsx = new Resampler();
629 rsx.rate = cast(double)srcrate/cast(double)destrate;
633 while (hasInputData && rsx.freeCount > 0) {
634 rsx.writeSampleFixed(getS16Sample(), 1);
636 // get data out of resampler
637 while (rsx.sampleCount > 0) {
638 auto smp = rsx.sample; // 16-bit sample
640 fo.writeNum!short(smp);
642 if (!hasInputData) break;