3 Copyright (C) 2010 and later Cockos Incorporated
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
21 You may also distribute this software under the LGPL v2 or later.
31 #define PI 3.1415926535897932384626433832795
34 class WDL_Resampler::WDL_Resampler_IIRFilter
37 WDL_Resampler_IIRFilter()
42 ~WDL_Resampler_IIRFilter()
48 memset(m_hist
,0,sizeof(m_hist
));
51 void setParms(double fpos
, double Q
)
53 if (fabs(fpos
-m_fpos
)<0.000001) return;
56 double pos
= fpos
* PI
;
60 double alpha
=spos
/(2.0*Q
);
62 double sc
=1.0/( 1 + alpha
);
64 m_b2
= m_b0
= m_b1
*0.5;
65 m_a1
= -2 * cpos
* sc
;
70 void Apply(WDL_ResampleSample
*in1
, WDL_ResampleSample
*out1
, int ns
, int span
, int w
)
72 double b0
=m_b0
,b1
=m_b1
,b2
=m_b2
,a1
=m_a1
,a2
=m_a2
;
73 double *hist
=m_hist
[w
];
78 double out
= (double) ( in
*b0
+ hist
[0]*b1
+ hist
[1]*b2
- hist
[2]*a1
- hist
[3]*a2
);
79 hist
[1]=hist
[0]; hist
[0]=in
;
80 hist
[3]=hist
[2]; *out1
= hist
[2]=denormal_filter_double(out
);
89 double m_b0
,m_b1
,m_b2
;
90 double m_hist
[WDL_RESAMPLE_MAX_FILTERS
*WDL_RESAMPLE_MAX_NCH
][4];
94 void inline WDL_Resampler::SincSample(WDL_ResampleSample
*outptr
, const WDL_ResampleSample
*inptr
, double fracpos
, int nch
, const WDL_SincFilterSample
*filter
, int filtsz
)
96 const int oversize
=m_lp_oversize
;
99 const int ifpos
=(int)fracpos
;
100 filter
+= (oversize
-ifpos
) * filtsz
;
104 for (x
= 0; x
< nch
; x
++)
106 double sum
=0.0,sum2
=0.0;
107 const WDL_SincFilterSample
*fptr2
=filter
;
108 const WDL_SincFilterSample
*fptr
=fptr2
- filtsz
;
109 const WDL_ResampleSample
*iptr
=inptr
+x
;
113 sum
+= fptr
[0]*iptr
[0];
114 sum2
+= fptr2
[0]*iptr
[0];
115 sum
+= fptr
[1]*iptr
[nch
];
116 sum2
+= fptr2
[1]*iptr
[nch
];
121 outptr
[x
]=sum
*fracpos
+ sum2
*(1.0-fracpos
);
126 void inline WDL_Resampler::SincSample1(WDL_ResampleSample
*outptr
, const WDL_ResampleSample
*inptr
, double fracpos
, const WDL_SincFilterSample
*filter
, int filtsz
)
128 const int oversize
=m_lp_oversize
;
130 const int ifpos
=(int)fracpos
;
133 double sum
=0.0,sum2
=0.0;
134 const WDL_SincFilterSample
*fptr2
=filter
+ (oversize
-ifpos
) * filtsz
;
135 const WDL_SincFilterSample
*fptr
=fptr2
- filtsz
;
136 const WDL_ResampleSample
*iptr
=inptr
;
140 sum
+= fptr
[0]*iptr
[0];
141 sum2
+= fptr2
[0]*iptr
[0];
142 sum
+= fptr
[1]*iptr
[1];
143 sum2
+= fptr2
[1]*iptr
[1];
148 outptr
[0]=sum
*fracpos
+sum2
*(1.0-fracpos
);
151 void inline WDL_Resampler::SincSample2(WDL_ResampleSample
*outptr
, const WDL_ResampleSample
*inptr
, double fracpos
, const WDL_SincFilterSample
*filter
, int filtsz
)
153 const int oversize
=m_lp_oversize
;
155 const int ifpos
=(int)fracpos
;
158 const WDL_SincFilterSample
*fptr2
=filter
+ (oversize
-ifpos
) * filtsz
;
159 const WDL_SincFilterSample
*fptr
=fptr2
- filtsz
;
165 const WDL_ResampleSample
*iptr
=inptr
;
169 sum
+= fptr
[0]*iptr
[0];
170 sum2
+= fptr
[0]*iptr
[1];
171 sumb
+= fptr2
[0]*iptr
[0];
172 sum2b
+= fptr2
[0]*iptr
[1];
173 sum
+= fptr
[1]*iptr
[2];
174 sum2
+= fptr
[1]*iptr
[3];
175 sumb
+= fptr2
[1]*iptr
[2];
176 sum2b
+= fptr2
[1]*iptr
[3];
181 outptr
[0]=sum
*fracpos
+ sumb
*(1.0-fracpos
);
182 outptr
[1]=sum2
*fracpos
+ sum2b
*(1.0-fracpos
);
188 WDL_Resampler::WDL_Resampler()
191 m_filterpos
=0.693f
; // .792 ?
200 m_filter_coeffs_size
=0;
210 WDL_Resampler::~WDL_Resampler()
215 void WDL_Resampler::Reset(double fracpos
)
220 m_samples_in_rsinbuf
=0;
221 if (m_iirfilter
) m_iirfilter
->Reset();
224 void WDL_Resampler::SetMode(bool interp
, int filtercnt
, bool sinc
, int sinc_size
, int sinc_interpsize
)
226 m_sincsize
= sinc
&& sinc_size
>= 4 ? sinc_size
> 8192 ? 8192 : (sinc_size
&~1) : 0;
227 m_sincoversize
= m_sincsize
? (sinc_interpsize
<= 1 ? 1 : sinc_interpsize
>=8192 ? 8192 : sinc_interpsize
) : 1;
229 m_filtercnt
= m_sincsize
? 0 : (filtercnt
<=0?0 : filtercnt
>= WDL_RESAMPLE_MAX_FILTERS
? WDL_RESAMPLE_MAX_FILTERS
: filtercnt
);
230 m_interp
=interp
&& !m_sincsize
;
232 // sprintf(buf,"setting interp=%d, filtercnt=%d, sinc=%d,%d\n",m_interp,m_filtercnt,m_sincsize,m_sincoversize);
233 // OutputDebugString(buf);
237 m_filter_coeffs
.Resize(0);
238 m_filter_coeffs_size
=0;
247 void WDL_Resampler::SetRates(double rate_in
, double rate_out
)
249 if (rate_in
<1.0) rate_in
=1.0;
250 if (rate_out
<1.0) rate_out
=1.0;
251 if (rate_in
!= m_sratein
|| rate_out
!= m_srateout
)
255 m_ratio
=m_sratein
/ m_srateout
;
260 void WDL_Resampler::BuildLowPass(double filtpos
) // only called in sinc modes
262 const int wantsize
=m_sincsize
;
263 const int wantinterp
=m_sincoversize
;
265 if (m_filter_ratio
!=filtpos
||
266 m_filter_coeffs_size
!= wantsize
||
267 m_lp_oversize
!= wantinterp
)
269 m_lp_oversize
= wantinterp
;
270 m_filter_ratio
=filtpos
;
272 // build lowpass filter
273 const int allocsize
= wantsize
*(m_lp_oversize
+1);
274 WDL_SincFilterSample
*cfout
=m_filter_coeffs
.Resize(allocsize
);
275 if (m_filter_coeffs
.GetSize()==allocsize
)
277 m_filter_coeffs_size
=wantsize
;
279 const double dwindowpos
= 2.0 * PI
/(double)wantsize
;
280 const double dsincpos
= PI
* filtpos
; // filtpos is outrate/inrate, i.e. 0.5 is going to half rate
281 const int hwantsize
=wantsize
/2;
283 double filtpower
=0.0;
284 WDL_SincFilterSample
*ptrout
= cfout
;
286 for (slice
=0;slice
<=wantinterp
;slice
++)
288 const double frac
= slice
/ (double)wantinterp
;
289 const int center_x
= slice
== 0 ? hwantsize
: slice
== wantinterp
? hwantsize
-1 : -1;
292 for (x
=0;x
<wantsize
;x
++)
296 // we know this will be 1.0
301 const double xfrac
= frac
+ x
;
302 const double windowpos
= dwindowpos
* xfrac
;
303 const double sincpos
= dsincpos
* (xfrac
- hwantsize
);
305 // blackman-harris * sinc
306 const double val
= (0.35875 - 0.48829 * cos(windowpos
) + 0.14128 * cos(2*windowpos
) - 0.01168 * cos(3*windowpos
)) * sin(sincpos
) / sincpos
;
307 if (slice
<wantinterp
) filtpower
+=val
;
308 *ptrout
++ = (WDL_SincFilterSample
)val
;
314 filtpower
= wantinterp
/(filtpower
+1.0);
316 for (x
= 0; x
< allocsize
; x
++)
318 cfout
[x
] = (WDL_SincFilterSample
) (cfout
[x
]*filtpower
);
321 else m_filter_coeffs_size
=0;
326 double WDL_Resampler::GetCurrentLatency()
328 double v
=((double)m_samples_in_rsinbuf
-m_filtlatency
)/m_sratein
;
334 int WDL_Resampler::ResamplePrepare(int out_samples
, int nch
, WDL_ResampleSample
**inbuffer
)
336 if (nch
> WDL_RESAMPLE_MAX_NCH
|| nch
< 1) return 0;
339 if (m_sincsize
>1) fsize
= m_sincsize
;
342 if (hfs
>1 && m_samples_in_rsinbuf
<hfs
-1)
344 m_filtlatency
+=hfs
-1 - m_samples_in_rsinbuf
;
346 m_samples_in_rsinbuf
=hfs
-1;
348 if (m_samples_in_rsinbuf
>0)
350 WDL_ResampleSample
*p
= m_rsinbuf
.Resize(m_samples_in_rsinbuf
*nch
,false);
351 memset(p
,0,sizeof(WDL_ResampleSample
)*m_rsinbuf
.GetSize());
357 if (!m_feedmode
) sreq
= (int)(m_ratio
* out_samples
) + 4 + fsize
- m_samples_in_rsinbuf
;
358 else sreq
= out_samples
;
363 m_rsinbuf
.Resize((m_samples_in_rsinbuf
+sreq
)*nch
,false);
365 int sz
= m_rsinbuf
.GetSize()/(nch
?nch
:1) - m_samples_in_rsinbuf
;
371 goto again
; // try again with half the size
373 // todo: notify of error?
377 *inbuffer
= m_rsinbuf
.Get() + m_samples_in_rsinbuf
*nch
;
379 m_last_requested
=sreq
;
385 int WDL_Resampler::ResampleOut(WDL_ResampleSample
*out
, int nsamples_in
, int nsamples_out
, int nch
)
387 if (nch
> WDL_RESAMPLE_MAX_NCH
|| nch
< 1) return 0;
391 if (m_ratio
> 1.0 && nsamples_in
> 0) // filter input
393 if (!m_iirfilter
) m_iirfilter
= new WDL_Resampler_IIRFilter
;
396 m_iirfilter
->setParms((1.0/m_ratio
)*m_filterpos
,m_filterq
);
398 WDL_ResampleSample
*buf
=(WDL_ResampleSample
*)m_rsinbuf
.Get() + m_samples_in_rsinbuf
*nch
;
401 for (x
=0; x
< nch
; x
++)
402 for (a
= 0; a
< n
; a
++)
403 m_iirfilter
->Apply(buf
+x
,buf
+x
,nsamples_in
,nch
,offs
++);
407 // prevent the caller from corrupting the internal state
408 m_samples_in_rsinbuf
+= nsamples_in
< m_last_requested
? nsamples_in
: m_last_requested
;
410 int rsinbuf_availtemp
= m_samples_in_rsinbuf
;
412 if (nsamples_in
< m_last_requested
) // flush out to ensure we can deliver
414 int fsize
=(m_last_requested
-nsamples_in
)*2 + m_sincsize
*2;
416 int alloc_size
=(m_samples_in_rsinbuf
+fsize
)*nch
;
417 WDL_ResampleSample
*zb
=m_rsinbuf
.Resize(alloc_size
,false);
418 if (m_rsinbuf
.GetSize()==alloc_size
)
420 memset(zb
+m_samples_in_rsinbuf
*nch
,0,fsize
*nch
*sizeof(WDL_ResampleSample
));
421 rsinbuf_availtemp
= m_samples_in_rsinbuf
+fsize
;
426 double srcpos
=m_fracpos
;
427 double drspos
= m_ratio
;
428 WDL_ResampleSample
*localin
= m_rsinbuf
.Get();
430 WDL_ResampleSample
*outptr
=out
;
436 if (m_sincsize
) // sinc interpolating
438 if (m_ratio
> 1.0) BuildLowPass(1.0 / (m_ratio
*1.03));
439 else BuildLowPass(1.0);
441 int filtsz
=m_filter_coeffs_size
;
442 int filtlen
= rsinbuf_availtemp
- filtsz
;
443 outlatadj
=filtsz
/2-1;
444 WDL_SincFilterSample
*filter
=m_filter_coeffs
.Get();
450 int ipos
= (int)srcpos
;
452 if (ipos
>= filtlen
-1) break; // quit decoding, not enough input samples
454 SincSample1(outptr
,localin
+ ipos
,srcpos
-ipos
,filter
,filtsz
);
464 int ipos
= (int)srcpos
;
466 if (ipos
>= filtlen
-1) break; // quit decoding, not enough input samples
468 SincSample2(outptr
,localin
+ ipos
*2,srcpos
-ipos
,filter
,filtsz
);
478 int ipos
= (int)srcpos
;
480 if (ipos
>= filtlen
-1) break; // quit decoding, not enough input samples
482 SincSample(outptr
,localin
+ ipos
*nch
,srcpos
-ipos
,nch
,filter
,filtsz
);
489 else if (!m_interp
) // point sampling
495 int ipos
= (int)srcpos
;
496 if (ipos
>= rsinbuf_availtemp
) break; // quit decoding, not enough input samples
498 *outptr
++ = localin
[ipos
];
507 int ipos
= (int)srcpos
;
508 if (ipos
>= rsinbuf_availtemp
) break; // quit decoding, not enough input samples
512 outptr
[0] = localin
[ipos
];
513 outptr
[1] = localin
[ipos
+1];
522 int ipos
= (int)srcpos
;
523 if (ipos
>= rsinbuf_availtemp
) break; // quit decoding, not enough input samples
525 memcpy(outptr
,localin
+ ipos
*nch
,nch
*sizeof(WDL_ResampleSample
));
531 else // linear interpolation
537 int ipos
= (int)srcpos
;
538 double fracpos
=srcpos
-ipos
;
540 if (ipos
>= rsinbuf_availtemp
-1)
542 break; // quit decoding, not enough input samples
545 double ifracpos
=1.0-fracpos
;
546 WDL_ResampleSample
*inptr
= localin
+ ipos
;
547 *outptr
++ = inptr
[0]*(ifracpos
) + inptr
[1]*(fracpos
);
556 int ipos
= (int)srcpos
;
557 double fracpos
=srcpos
-ipos
;
559 if (ipos
>= rsinbuf_availtemp
-1)
561 break; // quit decoding, not enough input samples
564 double ifracpos
=1.0-fracpos
;
565 WDL_ResampleSample
*inptr
= localin
+ ipos
*2;
566 outptr
[0] = inptr
[0]*(ifracpos
) + inptr
[2]*(fracpos
);
567 outptr
[1] = inptr
[1]*(ifracpos
) + inptr
[3]*(fracpos
);
577 int ipos
= (int)srcpos
;
578 double fracpos
=srcpos
-ipos
;
580 if (ipos
>= rsinbuf_availtemp
-1)
582 break; // quit decoding, not enough input samples
585 double ifracpos
=1.0-fracpos
;
587 WDL_ResampleSample
*inptr
= localin
+ ipos
*nch
;
590 *outptr
++ = inptr
[0]*(ifracpos
) + inptr
[nch
]*(fracpos
);
602 if (m_ratio
< 1.0 && ret
>0) // filter output
604 if (!m_iirfilter
) m_iirfilter
= new WDL_Resampler_IIRFilter
;
606 m_iirfilter
->setParms(m_ratio
*m_filterpos
,m_filterq
);
610 for (x
=0; x
< nch
; x
++)
611 for (a
= 0; a
< n
; a
++)
612 m_iirfilter
->Apply(out
+x
,out
+x
,ret
,nch
,offs
++);
618 if (ret
>0 && rsinbuf_availtemp
>m_samples_in_rsinbuf
) // we had to pad!!
620 // check for the case where rsinbuf_availtemp>m_samples_in_rsinbuf, decrease ret down to actual valid samples
621 double adj
=(srcpos
-m_samples_in_rsinbuf
+ outlatadj
) / drspos
;
624 ret
-= (int) (adj
+ 0.5);
629 int isrcpos
=(int)srcpos
;
630 if (isrcpos
> m_samples_in_rsinbuf
) isrcpos
=m_samples_in_rsinbuf
;
631 m_fracpos
= srcpos
- isrcpos
;
632 m_samples_in_rsinbuf
-= isrcpos
;
633 if (m_samples_in_rsinbuf
<= 0) m_samples_in_rsinbuf
=0;
635 memmove(localin
, localin
+ isrcpos
*nch
,m_samples_in_rsinbuf
*sizeof(WDL_ResampleSample
)*nch
);