egedit: do not save cursor movement in undo -- this is my stupid habit, and it comple...
[iv.d.git] / audioresampler.d
blob2eb28888628787a36dd31ed7def772d0c819b5b4
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*/;
17 import iv.alice;
20 final class Resampler {
21 nothrow:
22 public enum Quality {
23 Zoh,
24 Blep,
25 Linear,
26 Blam,
27 Cubic,
28 Sinc,
31 private:
32 int writePos, writeFilled;
33 int readPos, readFilled;
34 float phase;
35 float phaseInc;
36 float invPhase;
37 float invPhaseInc;
38 ubyte xquality;
39 byte delayAdded;
40 byte delayRemoved;
41 float lastAmp;
42 float accum;
43 float[BufferSize*2] bufferIn;
44 float[BufferSize+SincWidth*2-1] bufferOut;
46 public:
47 this () {
48 writePos = SincWidth-1;
49 writeFilled = 0;
50 readPos = 0;
51 readFilled = 0;
52 phase = 0;
53 phaseInc = 0;
54 invPhase = 0;
55 invPhaseInc = 0;
56 xquality = Quality.max;
57 delayAdded = -1;
58 delayRemoved = -1;
59 lastAmp = 0;
60 accum = 0;
61 bufferIn[] = 0;
62 bufferOut[] = 0;
65 // duplicate resampler object
66 Resampler dup () const {
67 auto res = new Resampler();
68 res.copyFrom(this);
69 return res;
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;
79 phase = rsrc.phase;
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;
87 accum = rsrc.accum;
88 bufferIn[] = rsrc.bufferIn[];
89 bufferOut[] = rsrc.bufferOut[];
92 // reset this resampler
93 void clear () {
94 writePos = SincWidth-1;
95 writeFilled = 0;
96 readPos = 0;
97 readFilled = 0;
98 phase = 0;
99 delayAdded = -1;
100 delayRemoved = -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) {
106 invPhase = 0;
107 lastAmp = 0;
108 accum = 0;
109 //memset(bufferOut.ptr, 0, bufferOut.sizeof);
110 bufferOut[] = 0;
114 Quality quality () {
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)
125 readPos = 0;
126 readFilled = 0;
127 lastAmp = 0;
128 accum = 0;
129 bufferOut[] = 0;
131 delayAdded = -1;
132 delayRemoved = -1;
134 xquality = cast(ubyte)xquality;
137 // return number of samples that resampler is able to accept before overflowing
138 int freeCount () {
139 return BufferSize-writeFilled;
142 // return number of samples that resampler is able to emit now
143 int sampleCount () {
144 if (readFilled < 1 && ((xquality != Quality.Blep && xquality != Quality.Blam) || invPhaseInc)) doFillAndRemoveDelay();
145 return readFilled;
148 // get processed sample out of resampler
149 short sample () {
150 int smp;
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);
155 } else {
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;
170 } else {
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;
182 if (decay) {
183 import core.stdc.math : fabs;
184 accum -= accum*(1.0f/8192.0f);
185 if (fabs(accum) < 1e-20f) accum = 0;
188 --readFilled;
189 readPos = (readPos+1)%BufferSize;
193 // is resampler ready to emiting output samples?
194 // note that buffer can contain unread samples, it is ok
195 bool ready () {
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;
206 // feed resampler
207 void writeSample (short s) {
208 if (delayAdded < 0) {
209 delayAdded = 0;
210 writeFilled = inputDelay();
212 if (writeFilled < BufferSize) {
213 float s32 = s;
214 s32 *= 256.0;
215 bufferIn.ptr[writePos] = s32;
216 bufferIn.ptr[writePos+BufferSize] = s32;
217 ++writeFilled;
218 writePos = (writePos+1)%BufferSize;
222 // feed resampler
223 void writeSampleFixed (int s, ubyte depth) {
224 if (delayAdded < 0) {
225 delayAdded = 0;
226 writeFilled = inputDelay();
228 if (writeFilled < BufferSize) {
229 float s32 = s;
230 s32 /= cast(double)(1<<(depth-1));
231 bufferIn.ptr[writePos] = s32;
232 bufferIn.ptr[writePos+BufferSize] = s32;
233 ++writeFilled;
234 writePos = (writePos+1)%BufferSize;
238 enum paddingSize = SincWidth-1;
240 // ////////////////////////////////////////////////////////////////////// //
241 private:
242 int minFilled () {
243 switch (xquality) {
244 default:
245 case Quality.Zoh:
246 case Quality.Blep:
247 return 1;
248 case Quality.Linear:
249 case Quality.Blam:
250 return 2;
251 case Quality.Cubic:
252 return 4;
253 case Quality.Sinc:
254 return SincWidth*2;
258 int inputDelay () {
259 switch (xquality) {
260 default:
261 case Quality.Zoh:
262 case Quality.Blep:
263 case Quality.Linear:
264 case Quality.Blam:
265 return 0;
266 case Quality.Cubic:
267 return 1;
268 case Quality.Sinc:
269 return SincWidth-1;
273 int outputDelay () {
274 switch (xquality) {
275 default:
276 case Quality.Zoh:
277 case Quality.Linear:
278 case Quality.Cubic:
279 case Quality.Sinc:
280 return 0;
281 case Quality.Blep:
282 case Quality.Blam:
283 return SincWidth-1;
287 int doZoh (float** outbuf, float* outbufend) {
288 int ccinsize = writeFilled;
289 const(float)* inbuf = bufferIn.ptr+BufferSize+writePos-writeFilled;
290 int used = 0;
291 ccinsize -= 1;
292 if (ccinsize > 0) {
293 float* ccoutbuf = *outbuf;
294 const(float)* ccinbuf = inbuf;
295 const(float)* ccinbufend = ccinbuf+ccinsize;
296 float ccPhase = phase;
297 float ccPhaseInc = phaseInc;
298 do {
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);
307 phase = ccPhase;
308 *outbuf = ccoutbuf;
309 used = cast(int)(ccinbuf-inbuf);
310 writeFilled -= used;
312 return used;
315 int doBlep (float** outbuf, float* outbufend) {
316 int ccinsize = writeFilled;
317 const(float)* inbuf = bufferIn.ptr+BufferSize+writePos-writeFilled;
318 int used = 0;
319 ccinsize -= 1;
320 if (ccinsize > 0) {
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;
329 do {
330 import core.stdc.math : fmod;
331 if (ccoutbuf+SincWidth*2 > outbufend) break;
332 float sample = (*ccinbuf++)-ccLastAmp;
333 if (sample) {
334 float[SincWidth*2] kernel = void;
335 float kernelSum = 0.0f;
336 int phaseReduced = cast(int)(ccInvPhase*Resolution);
337 int phaseAdj = phaseReduced*step/Resolution;
338 int i = SincWidth;
339 for (; i >= -SincWidth+1; --i) {
340 int pos = i*step;
341 int winpos = i*winstep;
342 kernelSum += kernel.ptr[i+SincWidth-1] = sincLut[abs(phaseAdj-pos)]*windowLut[abs(phaseReduced-winpos)];
344 ccLastAmp += sample;
345 sample /= kernelSum;
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;
353 lastAmp = ccLastAmp;
354 *outbuf = ccoutbuf;
355 used = cast(int)(ccinbuf-inbuf);
356 writeFilled -= used;
358 return used;
361 int doLinear (float** outbuf, float* outbufend) {
362 int ccinsize = writeFilled;
363 const(float)* inbuf = bufferIn.ptr+BufferSize+writePos-writeFilled;
364 int used = 0;
365 ccinsize -= 2;
366 if (ccinsize > 0) {
367 float* ccoutbuf = *outbuf;
368 const(float)* ccinbuf = inbuf;
369 const(float)* ccinbufend = ccinbuf+ccinsize;
370 float ccPhase = phase;
371 float ccPhaseInc = phaseInc;
372 do {
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);
381 phase = ccPhase;
382 *outbuf = ccoutbuf;
383 used = cast(int)(ccinbuf-inbuf);
384 writeFilled -= used;
386 return used;
389 int doBlam (float** outbuf, float* outbufend) {
390 int ccinsize = writeFilled;
391 const(float)*inbuf = bufferIn.ptr+BufferSize+writePos-writeFilled;
392 int used = 0;
393 ccinsize -= 2;
394 if (ccinsize > 0) {
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;
405 do {
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;
410 sample -= ccLastAmp;
411 if (sample) {
412 float[SincWidth*2] kernel = void;
413 float kernelSum = 0.0f;
414 int phaseReduced = cast(int)(ccInvPhase*Resolution);
415 int phaseAdj = phaseReduced*step/Resolution;
416 int i = SincWidth;
417 for (; i >= -SincWidth+1; --i) {
418 int pos = i*step;
419 int winpos = i*winstep;
420 kernelSum += kernel.ptr[i+SincWidth-1] = sincLut[abs(phaseAdj-pos)]*windowLut[abs(phaseReduced-winpos)];
422 ccLastAmp += sample;
423 sample /= kernelSum;
424 for (i = 0; i < SincWidth*2; ++i) ccoutbuf[i] += sample*kernel.ptr[i];
426 if (ccInvPhaseInc < 1.0f) {
427 ++ccinbuf;
428 ccInvPhase += ccInvPhaseInc;
429 ccoutbuf += cast(int)ccInvPhase;
430 ccInvPhase = fmod(ccInvPhase, 1.0f);
431 } else {
432 ccPhase += ccPhaseInc;
433 ++ccoutbuf;
434 ccinbuf += cast(int)ccPhase;
435 ccPhase = fmod(ccPhase, 1.0f);
437 } while (ccinbuf < ccinbufend);
438 phase = ccPhase;
439 invPhase = ccInvPhase;
440 lastAmp = ccLastAmp;
441 *outbuf = ccoutbuf;
442 used = cast(int)(ccinbuf-inbuf);
443 writeFilled -= used;
445 return used;
448 int doCubic (float** outbuf, float* outbufend) {
449 int ccinsize = writeFilled;
450 const(float)*inbuf = bufferIn.ptr+BufferSize+writePos-writeFilled;
451 int used = 0;
452 ccinsize -= 4;
453 if (ccinsize > 0) {
454 float* ccoutbuf = *outbuf;
455 const(float)* ccinbuf = inbuf;
456 const(float)* ccinbufend = ccinbuf+ccinsize;
457 float ccPhase = phase;
458 float ccPhaseInc = phaseInc;
459 do {
460 import core.stdc.math : fmod;
461 int i;
462 float sample;
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);
471 phase = ccPhase;
472 *outbuf = ccoutbuf;
473 used = cast(int)(ccinbuf-inbuf);
474 writeFilled -= used;
476 return used;
479 int doSinc (float** outbuf, float* outbufend) {
480 int ccinsize = writeFilled;
481 const(float)*inbuf = bufferIn.ptr+BufferSize+writePos-writeFilled;
482 int used = 0;
483 ccinsize -= SincWidth*2;
484 if (ccinsize > 0) {
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;
492 do {
493 import core.stdc.math : fmod;
494 float[SincWidth*2] kernel = void;
495 float kernelSum = 0.0;
496 int i = SincWidth;
497 int phaseReduced = cast(int)(ccPhase*Resolution);
498 int phaseAdj = phaseReduced*step/Resolution;
499 float sample;
500 if (ccoutbuf >= outbufend) break;
501 for (; i >= -SincWidth+1; --i) {
502 int pos = i*step;
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);
512 phase = ccPhase;
513 *outbuf = ccoutbuf;
514 used = cast(int)(ccinbuf-inbuf);
515 writeFilled -= used;
517 return used;
520 void doFill () {
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) {
530 case Quality.Zoh:
531 doZoh(&ccoutbuf, ccoutbuf+ccWriteSize);
532 break;
533 case Quality.Blep:
534 int used;
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);
541 if (!used) return;
542 break;
543 case Quality.Linear:
544 doLinear(&ccoutbuf, ccoutbuf+ccWriteSize);
545 break;
546 case Quality.Blam:
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;
555 break;
556 case Quality.Cubic:
557 doCubic(&ccoutbuf, ccoutbuf+ccWriteSize);
558 break;
559 case Quality.Sinc:
560 doSinc(&ccoutbuf, ccoutbuf+ccWriteSize);
561 break;
562 default: assert(0, "wtf?!");
564 readFilled += ccoutbuf-bufferOut.ptr-ccWritePos;
568 void doFillAndRemoveDelay () {
569 doFill();
570 if (delayRemoved < 0) {
571 int delay = outputDelay();
572 delayRemoved = 0;
573 while (delay--) removeSample(true);
577 // ////////////////////////////////////////////////////////////////////// //
578 enum PI = 3.14159265358979323846;
579 enum Shift = 10;
580 enum ShiftExtra = 8;
581 enum Resolution = 1<<Shift;
582 enum ResolutionExtra = 1<<(Shift+ShiftExtra);
583 enum SincWidth = 16;
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 // ////////////////////////////////////////////////////////////////////// //
592 static:
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);
616 x = 0.0;
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);
627 Resampler rsx;
628 rsx = new Resampler();
629 rsx.rate = cast(double)srcrate/cast(double)destrate;
631 for (;;) {
632 // feed resampler
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
639 rsx.removeSample();
640 fo.writeNum!short(smp);
642 if (!hasInputData) break;