1 /*
2  * Copyright (c) 2009, The MilkyTracker Team.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright notice,
9  *   this list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * - Neither the name of the <ORGANIZATION> nor the names of its contributors
14  *   may be used to endorse or promote products derived from this software
15  *   without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  *  ResamplerSinc.h
32  *  MilkyPlay
33  *
34  *  Created by Peter Barth on 03.01.08.
35  *
36  */
37 
38 #include <math.h>
39 
40 /*
41  * Sinc resamplers based on:
42  * http://www.cs.princeton.edu/courses/archive/spr07/cos325/src/TimeStuf/srconvrt.c
43  *
44  */
45 
46 #ifndef M_PI
47 #define M_PI 3.14159265358979323846
48 #endif
49 
50 #define fpmul MP_FP_MUL
51 
52 #define advancePos(CHNsmppos, CHNflags, CHNloopstart, CHNloopend, CHNloopendcopy) \
53 	if (((((CHNflags&3) == 0 || (CHNflags&3) == 1)) && !(CHNflags&ChannelMixer::MP_SAMPLE_BACKWARD)) || \
54 		((CHNflags&3) == 2 && (CHNflags&ChannelMixer::MP_SAMPLE_BACKWARD) == 0)) \
55 	{ \
56 		CHNsmppos++; \
57 		/* stop playing if necessary */ \
58 		if (CHNsmppos>=CHNloopend) \
59 		{ \
60 			if ((CHNflags & 3) == 0) \
61 			{ \
62 				if (CHNflags & ChannelMixer::MP_SAMPLE_ONESHOT) \
63 				{ \
64 					CHNflags &= ~ChannelMixer::MP_SAMPLE_ONESHOT; \
65 					CHNflags |= 1; \
66 					CHNloopend = CHNloopendcopy; \
67 					CHNsmppos = CHNloopstart; \
68 				} \
69 				else \
70 				{ \
71 					CHNflags&=~ChannelMixer::MP_SAMPLE_PLAY; \
72 				} \
73 			} \
74 			else if ((CHNflags & 3) == 1) \
75 			{ \
76 				CHNsmppos = CHNloopstart; \
77 			} \
78 			else \
79 			{ \
80 				CHNflags|=ChannelMixer::MP_SAMPLE_BACKWARD; \
81 				CHNsmppos = CHNloopend-1; \
82 			} \
83 		}\
84 	} \
85 	/* bi-dir loop */ \
86 	else \
87 	{ \
88 		CHNsmppos--; \
89 		if (CHNloopstart>CHNsmppos) \
90 		{ \
91 			if ((CHNflags & 3) == 0) \
92 			{ \
93 				CHNflags&=~ChannelMixer::MP_SAMPLE_PLAY; \
94 			} \
95 			else if ((CHNflags & 3) == 1) \
96 			{ \
97 				CHNsmppos = CHNloopend-1; \
98 			} \
99 			else \
100 			{ \
101 				CHNflags&=~ChannelMixer::MP_SAMPLE_BACKWARD; \
102 				CHNsmppos = CHNloopstart; \
103 			} \
104 		} \
105 	}
106 
107 // double precision sinc without window function
108 
109 template<bool ramping, mp_sint32 windowSize, class bufferType, mp_uint32 shift>
110 class SincResamplerDummy
111 {
112 private:
sinc(double x)113 	static inline double sinc(double x)
114 	{
115 		if (x==0.0)
116 			return 1.0;
117 		else
118 		{
119 			double temp = M_PI * x;
120 			return sin(temp) / (temp);
121 		}
122 	}
123 
124 	enum
125 	{
126 		WINDOWSIZE = windowSize, // must be even
127 		WIDTH = (WINDOWSIZE / 2)
128 	};
129 
130 public:
addBlock(mp_sint32 * buffer,ChannelMixer::TMixerChannel * chn,mp_uint32 count)131 	static inline void addBlock(mp_sint32* buffer, ChannelMixer::TMixerChannel* chn, mp_uint32 count)
132 	{
133 		const bufferType* sample = (const bufferType*)chn->sample;
134 
135 		mp_sint32 voll = chn->finalvoll;
136 		mp_sint32 volr = chn->finalvolr;
137 
138 		const mp_sint32 rampFromVolStepL = ramping ? chn->rampFromVolStepL : 0;
139 		const mp_sint32 rampFromVolStepR = ramping ? chn->rampFromVolStepR : 0;
140 
141 		mp_sint32 smppos = chn->smppos;
142 		mp_sint32 smpposfrac = chn->smpposfrac;
143 		const mp_sint32 smpadd = (chn->flags&ChannelMixer::MP_SAMPLE_BACKWARD) ? -chn->smpadd : chn->smpadd;
144 
145 		const mp_sint32 flags = chn->flags;
146 		const mp_sint32 loopstart = chn->loopstart;
147 		const mp_sint32 loopend = chn->loopend;
148 		const mp_sint32 loopendcopy = chn->loopendcopy;
149 		const mp_sint32 smplen = chn->smplen;
150 
151 		mp_sint32 fixedtimefrac = chn->fixedtimefrac;
152 		const mp_sint32 timeadd = chn->smpadd;
153 
154 		ChannelMixer::TMixerChannel pos(true);
155 
156 		while (count--)
157 		{
158 			double result = 0;
159 
160 			const double time_now = fixedtimefrac * (1.0 / 65536.0);
161 
162 			if (abs(smpadd)<65536)
163 			{
164 				pos.smppos = smppos;
165 				pos.loopstart = loopstart;
166 				pos.loopend = loopend;
167 				pos.loopendcopy = loopendcopy;
168 				pos.flags = smpadd < 0 ? (flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) : ((flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) | ChannelMixer::MP_SAMPLE_BACKWARD);
169 				// check whether we are outside loop points
170 				// if that's the case we're treating the sample as a normal finite signal
171 				// note that this is still not totally correct treatment
172 				const bool outSideLoop = !(((flags & 3) && pos.smppos >= loopstart && pos.smppos < loopend));
173 				if (outSideLoop)
174 				{
175 					pos.loopstart = 0;
176 					pos.loopend = smplen;
177 					pos.flags &= ~3;
178 				}
179 
180 				double time = time_now;
181 				if (!fixedtimefrac && (flags & ChannelMixer::MP_SAMPLE_BACKWARD))
182 					time = 1.0;
183 
184 				mp_sint32 j;
185 				for (j = 0; j<WIDTH; j++)
186 				{
187 					//double w = 0.42 - 0.5 * cos(2.0*M_PI*(WIDTH-1-j)/(WIDTH*2)) + 0.08*cos(4.0*M_PI*(WIDTH-1-j)/(WIDTH*2));
188 
189 					result += (sample[pos.smppos]) * sinc(time);
190 
191 					time++;
192 					advancePos(pos.smppos, pos.flags, pos.loopstart, pos.loopend, pos.loopendcopy);
193 					if (!(pos.flags & ChannelMixer::MP_SAMPLE_PLAY))
194 						break;
195 				}
196 
197 				pos.smppos = smppos;
198 				pos.flags = smpadd > 0 ? (flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) : ((flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) | ChannelMixer::MP_SAMPLE_BACKWARD);
199 				if (outSideLoop)
200 					pos.flags &= ~3;
201 
202 				time = time_now;
203 				if (!fixedtimefrac && (flags & ChannelMixer::MP_SAMPLE_BACKWARD))
204 					time = 1.0;
205 
206 				for (j = 1; j<WIDTH; j++)
207 				{
208 					//double w = 0.42 - 0.5 * cos(2.0*M_PI*(j-1+WIDTH)/(WIDTH*2)) + 0.08*cos(4.0*M_PI*(j-1+WIDTH)/(WIDTH*2));
209 
210 					advancePos(pos.smppos, pos.flags, pos.loopstart, pos.loopend, pos.loopendcopy);
211 					time--;
212 					if (!(pos.flags & ChannelMixer::MP_SAMPLE_PLAY))
213 						break;
214 
215 					result += (sample[pos.smppos]) * sinc(time);
216 				}
217 			}
218 			else
219 			{
220 				double factor = smpadd * (1.0 / 65536.0);
221 				double one_over_factor = 1.0 / factor;
222 
223 				pos.smppos = smppos;
224 				pos.loopstart = loopstart;
225 				pos.loopend = loopend;
226 				pos.loopendcopy = loopendcopy;
227 				pos.flags = smpadd < 0 ? (flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) : ((flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) | ChannelMixer::MP_SAMPLE_BACKWARD);
228 				// check whether we are outside loop points
229 				// if that's the case we're treating the sample as a normal finite signal
230 				// note that this is still not totally correct treatment
231 				const bool outSideLoop = !(((flags & 3) && pos.smppos >= loopstart && pos.smppos < loopend));
232 				if (outSideLoop)
233 				{
234 					pos.loopstart = 0;
235 					pos.loopend = smplen;
236 					pos.flags &= ~3;
237 				}
238 
239 				double time = time_now;
240 				if (!fixedtimefrac && (flags & ChannelMixer::MP_SAMPLE_BACKWARD))
241 					time = 1.0;
242 
243 				mp_sint32 j;
244 				for (j = 0; j<WIDTH; j++)
245 				{
246 					//double w = 0.42 - 0.5 * cos(2.0*M_PI*(WIDTH-1-j)/(WIDTH*2)) + 0.08*cos(4.0*M_PI*(WIDTH-1-j)/(WIDTH*2));
247 
248 					result += (sample[pos.smppos]) * one_over_factor * sinc(one_over_factor * time);
249 
250 					advancePos(pos.smppos, pos.flags, pos.loopstart, pos.loopend, pos.loopendcopy);
251 					time++;
252 					if (!(pos.flags & ChannelMixer::MP_SAMPLE_PLAY))
253 						break;
254 				}
255 
256 				pos.smppos = smppos;
257 				pos.flags = smpadd > 0 ? (flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) : ((flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) | ChannelMixer::MP_SAMPLE_BACKWARD);
258 				if (outSideLoop)
259 					pos.flags &= ~3;
260 
261 				time = time_now;
262 				if (!fixedtimefrac && (flags & ChannelMixer::MP_SAMPLE_BACKWARD))
263 					time = 1.0;
264 
265 				for (j = 1; j<WIDTH; j++)
266 				{
267 					//double w = 0.42 - 0.5 * cos(2.0*M_PI*(j-1+WIDTH)/(WIDTH*2)) + 0.08*cos(4.0*M_PI*(j-1+WIDTH)/(WIDTH*2));
268 
269 					advancePos(pos.smppos, pos.flags, pos.loopstart, pos.loopend, pos.loopendcopy);
270 					time--;
271 					if (!(pos.flags & ChannelMixer::MP_SAMPLE_PLAY))
272 						break;
273 
274 					result += (sample[pos.smppos]) * one_over_factor * sinc(one_over_factor * time);
275 				}
276 			}
277 
278 
279 			mp_sint32 final = (mp_sint32)(result*(1 << (16-shift)));
280 
281 			(*buffer++)+=((final*(voll>>15))>>15);
282 			(*buffer++)+=((final*(volr>>15))>>15);
283 
284 			if (ramping)
285 			{
286 				voll+=rampFromVolStepL;
287 				volr+=rampFromVolStepR;
288 			}
289 
290 			MP_INCREASESMPPOS(smppos, smpposfrac, smpadd, 16);
291 			fixedtimefrac=(fixedtimefrac+timeadd) & 65535;
292 		}
293 
294 		chn->smppos = smppos;
295 		chn->smpposfrac = smpposfrac;
296 
297 		chn->fixedtimefrac = fixedtimefrac;
298 
299 		if (ramping)
300 		{
301 			chn->finalvoll = voll;
302 			chn->finalvolr = volr;
303 		}
304 	}
305 
306 };
307 
308 template<bool ramping, mp_sint32 windowSize>
309 class ResamplerSinc : public ChannelMixer::ResamplerBase
310 {
311 private:
312 
313 public:
isRamping()314 	virtual bool isRamping() { return ramping; }
supportsFullChecking()315 	virtual bool supportsFullChecking() { return false; }
supportsNoChecking()316 	virtual bool supportsNoChecking() { return true; }
317 
addBlockNoCheck(mp_sint32 * buffer,ChannelMixer::TMixerChannel * chn,mp_uint32 count)318 	virtual void addBlockNoCheck(mp_sint32* buffer, ChannelMixer::TMixerChannel* chn, mp_uint32 count)
319 	{
320 		if (chn->flags & 4)
321 			SincResamplerDummy<ramping, windowSize, mp_sword, 16>::addBlock(buffer, chn, count);
322 		else
323 			SincResamplerDummy<ramping, windowSize, mp_sbyte, 8>::addBlock(buffer, chn, count);
324 	}
325 };
326 
327 // fixed point sinc with almost hamming window
328 
329 // you like that, eh?
330 #define SINCTAB ResamplerSincTableBase<windowSize>::sinc_table
331 #define WSIZE ResamplerSincTableBase<windowSize>::WIDTH
332 #define SPZCSHIFT ResamplerSincTableBase<windowSize>::SAMPLES_PER_ZERO_CROSSING_SHIFT
333 
334 #define SINC(x) \
335 	((abs(x)>>16)>=(WSIZE-1) ? 0 : \
336 	(SINCTAB[abs(x) >> (16-SPZCSHIFT)] + \
337 	fpmul((SINCTAB[(abs(x) >> (16-SPZCSHIFT)) + 1] - \
338 	SINCTAB[abs(x) >> (16-SPZCSHIFT)]), \
339 	(abs(x) >> (16-SPZCSHIFT)) & 65535)))
340 
341 // share sinc lookup table
342 template<mp_sint32 windowSize>
343 class ResamplerSincTableBase : public ChannelMixer::ResamplerBase
344 {
345 protected:
346 	enum
347 	{
348 		WINDOWSIZE = windowSize, // must be even
349 		WIDTH = (WINDOWSIZE / 2),
350 		SAMPLES_PER_ZERO_CROSSING_SHIFT = 10,
351 		SAMPLES_PER_ZERO_CROSSING = (1 << SAMPLES_PER_ZERO_CROSSING_SHIFT),
352 		TABLESIZE = SAMPLES_PER_ZERO_CROSSING*WIDTH,
353 	};
354 
355 	static mp_sint32* sinc_table;
356 
make_sinc()357 	void make_sinc()
358 	{
359 		mp_sint32 i;
360 		double temp,win_freq,win;
361 		win_freq = M_PI / WIDTH / SAMPLES_PER_ZERO_CROSSING;
362 		sinc_table[0] = 65536;
363 		for (i=1;i<WIDTH * SAMPLES_PER_ZERO_CROSSING;i++)   {
364 			temp = (double) i * M_PI / SAMPLES_PER_ZERO_CROSSING;
365 			win = 0.5 + 0.5 * cos(win_freq * i); // not quite true hamming window, but close
366 			sinc_table[i] = (mp_sint32)(((sin(temp) / temp) * win) * 65536.0);
367 		}
368 	}
369 
370 	static bool tableInit;
371 
ResamplerSincTableBase()372 	ResamplerSincTableBase()
373 	{
374 		if (!tableInit)
375 		{
376 			sinc_table = new mp_sint32[TABLESIZE];
377 			make_sinc();
378 			tableInit = true;
379 		}
380 	}
381 };
382 
383 template<mp_sint32 windowSize>
384 bool ResamplerSincTableBase<windowSize>::tableInit = false;
385 template<mp_sint32 windowSize>
386 mp_sint32* ResamplerSincTableBase<windowSize>::sinc_table = NULL;
387 
388 template<bool ramping, mp_sint32 windowSize, class bufferType, mp_uint32 shift>
389 class SincTableResamplerDummy : public ResamplerSincTableBase<windowSize>
390 {
391 public:
addBlock(mp_sint32 * buffer,ChannelMixer::TMixerChannel * chn,mp_uint32 count)392 	static inline void addBlock(mp_sint32* buffer, ChannelMixer::TMixerChannel* chn, mp_uint32 count)
393 	{
394 		const bufferType* sample = (const bufferType*)chn->sample;
395 
396 		mp_sint32 voll = chn->finalvoll;
397 		mp_sint32 volr = chn->finalvolr;
398 
399 		const mp_sint32 rampFromVolStepL = ramping ? chn->rampFromVolStepL : 0;
400 		const mp_sint32 rampFromVolStepR = ramping ? chn->rampFromVolStepR : 0;
401 
402 		mp_sint32 smppos = chn->smppos;
403 		mp_sint32 smpposfrac = chn->smpposfrac;
404 		const mp_sint32 smpadd = (chn->flags&ChannelMixer::MP_SAMPLE_BACKWARD) ? -chn->smpadd : chn->smpadd;
405 		const mp_sint32 rsmpadd = chn->rsmpadd;
406 
407 		const mp_sint32 flags = chn->flags;
408 		const mp_sint32 loopstart = chn->loopstart;
409 		const mp_sint32 loopend = chn->loopend;
410 		const mp_sint32 loopendcopy = chn->loopendcopy;
411 		const mp_sint32 smplen = chn->smplen;
412 
413 		mp_sint32 fixedtimefrac = chn->fixedtimefrac;
414 		const mp_sint32 timeadd = chn->smpadd;
415 
416 		const mp_sint32 negflags = smpadd < 0 ? (flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) : ((flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) | ChannelMixer::MP_SAMPLE_BACKWARD);
417 		const mp_sint32 posflags = smpadd > 0 ? (flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) : ((flags & ~ChannelMixer::MP_SAMPLE_BACKWARD) | ChannelMixer::MP_SAMPLE_BACKWARD);
418 
419 		mp_sint32 tmpsmppos;
420 		mp_sint32 tmpflags;
421 		mp_sint32 tmploopstart;
422 		mp_sint32 tmploopend;
423 
424 		if (timeadd < 65536)
425 		{
426 			while (count--)
427 			{
428 				mp_sint32 result = 0;
429 
430 				tmpsmppos = smppos;
431 				tmploopstart = loopstart;
432 				tmploopend = loopend;
433 				tmpflags = negflags;
434 				// check whether we are outside loop points
435 				// if that's the case we're treating the sample as a normal finite signal
436 				// note that this is still not totally correct treatment
437 				const bool outSideLoop = !(((flags & 3) && tmpsmppos >= loopstart && tmpsmppos < loopend));
438 				if (outSideLoop)
439 				{
440 					tmploopstart = 0;
441 					tmploopend = smplen;
442 					tmpflags &= ~3;
443 				}
444 
445 				mp_sint32 time = fixedtimefrac;
446 				if (!time && (flags & ChannelMixer::MP_SAMPLE_BACKWARD))
447 					time = 65536;
448 
449 				mp_sint32 j;
450 				for (j = 0; j<ResamplerSincTableBase<windowSize>::WIDTH; j++)
451 				{
452 					result += (sample[tmpsmppos] * SINC(time)) >> shift;
453 
454 					time+=65536;
455 					advancePos(tmpsmppos, tmpflags, tmploopstart, tmploopend, loopendcopy);
456 					if (!(tmpflags & ChannelMixer::MP_SAMPLE_PLAY))
457 						break;
458 				}
459 
460 				tmpsmppos = smppos;
461 				tmpflags = posflags;
462 				if (outSideLoop)
463 					tmpflags &= ~3;
464 
465 				time = fixedtimefrac;
466 				if (!time && (flags & ChannelMixer::MP_SAMPLE_BACKWARD))
467 					time = 65536;
468 
469 				for (j = 1; j<ResamplerSincTableBase<windowSize>::WIDTH; j++)
470 				{
471 					advancePos(tmpsmppos, tmpflags, tmploopstart, tmploopend, loopendcopy);
472 					time-=65536;
473 					if (!(tmpflags & ChannelMixer::MP_SAMPLE_PLAY))
474 						break;
475 
476 					result += (sample[tmpsmppos] * SINC(time)) >> shift;
477 				}
478 
479 
480 				(*buffer++)+=(((result)*(voll>>15))>>15);
481 				(*buffer++)+=(((result)*(volr>>15))>>15);
482 
483 				if (ramping)
484 				{
485 					voll+=rampFromVolStepL;
486 					volr+=rampFromVolStepR;
487 				}
488 
489 				MP_INCREASESMPPOS(smppos, smpposfrac, smpadd, 16);
490 				fixedtimefrac=(fixedtimefrac+timeadd) & 65535;
491 			}
492 		}
493 		else
494 		{
495 			while (count--)
496 			{
497 				mp_sint32 result = 0;
498 
499 				tmpsmppos = smppos;
500 				tmploopstart = loopstart;
501 				tmploopend = loopend;
502 				tmpflags = negflags;
503 				// check whether we are outside loop points
504 				// if that's the case we're treating the sample as a normal finite signal
505 				// note that this is still not totally correct treatment
506 				const bool outSideLoop = !(((flags & 3) && tmpsmppos >= loopstart && tmpsmppos < loopend));
507 				if (outSideLoop)
508 				{
509 					tmploopstart = 0;
510 					tmploopend = smplen;
511 					tmpflags &= ~3;
512 				}
513 
514 				mp_sint32 time = fpmul(fixedtimefrac, rsmpadd);
515 				if (!time && (flags & ChannelMixer::MP_SAMPLE_BACKWARD))
516 					time = 65536;
517 
518 				mp_sint32 j;
519 				for (j = 0; j<ResamplerSincTableBase<windowSize>::WIDTH; j++)
520 				{
521 					result += (sample[tmpsmppos] * fpmul(SINC(time), rsmpadd)) >> shift;
522 
523 					advancePos(tmpsmppos, tmpflags, tmploopstart, tmploopend, loopendcopy);
524 					time+=rsmpadd;
525 					if (!(tmpflags & ChannelMixer::MP_SAMPLE_PLAY))
526 						break;
527 				}
528 
529 				tmpsmppos = smppos;
530 				tmpflags = posflags;
531 				if (outSideLoop)
532 					tmpflags &= ~3;
533 
534 				time = fpmul(fixedtimefrac, rsmpadd);
535 				if (!time && (flags & ChannelMixer::MP_SAMPLE_BACKWARD))
536 					time = 65536;
537 
538 				for (j = 1; j<ResamplerSincTableBase<windowSize>::WIDTH; j++)
539 				{
540 					advancePos(tmpsmppos, tmpflags, tmploopstart, tmploopend, loopendcopy);
541 					time-=rsmpadd;
542 					if (!(tmpflags & ChannelMixer::MP_SAMPLE_PLAY))
543 						break;
544 
545 					result += (sample[tmpsmppos] * fpmul(SINC(time), rsmpadd)) >> shift;
546 				}
547 
548 				(*buffer++)+=(((result)*(voll>>15))>>15);
549 				(*buffer++)+=(((result)*(volr>>15))>>15);
550 
551 				if (ramping)
552 				{
553 					voll+=rampFromVolStepL;
554 					volr+=rampFromVolStepR;
555 				}
556 
557 				MP_INCREASESMPPOS(smppos, smpposfrac, smpadd, 16);
558 				fixedtimefrac=(fixedtimefrac+timeadd) & 65535;
559 			}
560 		}
561 
562 		chn->smppos = smppos;
563 		chn->smpposfrac = smpposfrac;
564 
565 		chn->fixedtimefrac = fixedtimefrac;
566 
567 		if (ramping)
568 		{
569 			chn->finalvoll = voll;
570 			chn->finalvolr = volr;
571 		}
572 	}
573 };
574 
575 template<bool ramping, mp_sint32 windowSize>
576 class ResamplerSincTable : public ResamplerSincTableBase<windowSize>
577 {
578 public:
ResamplerSincTable()579 	ResamplerSincTable() :
580 		ResamplerSincTableBase<windowSize>()
581 	{
582 	}
583 
isRamping()584 	virtual bool isRamping() { return ramping; }
supportsFullChecking()585 	virtual bool supportsFullChecking() { return false; }
supportsNoChecking()586 	virtual bool supportsNoChecking() { return true; }
587 
addBlockNoCheck(mp_sint32 * buffer,ChannelMixer::TMixerChannel * chn,mp_uint32 count)588 	virtual void addBlockNoCheck(mp_sint32* buffer, ChannelMixer::TMixerChannel* chn, mp_uint32 count)
589 	{
590 		if (chn->flags & 4)
591 			SincTableResamplerDummy<ramping, windowSize, mp_sword, 16>::addBlock(buffer, chn, count);
592 		else
593 			SincTableResamplerDummy<ramping, windowSize, mp_sbyte, 8>::addBlock(buffer, chn, count);
594 	}
595 };
596 
597 #undef SINC
598 
599 #undef SPZCSHIFT
600 #undef WSIZE
601 #undef SINCTAB
602 
603 #undef fpmul
604