Merge pull request #110 from tesselode/fixes
[wdl/wdl-ol.git] / WDL / simple_pitchshift.h
blobbfee486f0dbbdd74c07ab09d746d5548174fab2e
1 #ifndef _WDL_SIMPLEPITCHSHIFT_H_
2 #define _WDL_SIMPLEPITCHSHIFT_H_
5 #include "queue.h"
7 #ifndef WDL_SIMPLEPITCHSHIFT_SAMPLETYPE
8 #define WDL_SIMPLEPITCHSHIFT_SAMPLETYPE double
9 #endif
12 #ifdef WDL_SIMPLEPITCHSHIFT_PARENTCLASS
13 class WDL_SimplePitchShifter : public WDL_SIMPLEPITCHSHIFT_PARENTCLASS
14 #else
15 class WDL_SimplePitchShifter
16 #endif
18 public:
19 WDL_SimplePitchShifter()
21 m_last_nch=1;
22 m_srate=44100.0;
23 m_last_tempo=1.0;
24 m_last_shift=1.0;
25 m_qual=0;
27 Reset();
29 ~WDL_SimplePitchShifter() { }
31 void Reset()
33 m_hadinput=0;
34 m_pspos=0.0;
35 m_pswritepos=0;
36 m_latpos=0;
37 m_tempo_fracpos=0.0;
38 m_queue.Clear();
39 m_rsbuf.Resize(0,false);
40 m_psbuf.Resize(0,false);
43 bool IsReset()
45 return !m_queue.Available() && !m_hadinput;
50 void set_srate(double srate) { m_srate=srate; }
51 void set_nch(int nch) { if (m_last_nch!=nch) { m_queue.Clear(); m_last_nch=nch; m_tempo_fracpos=0.0; } }
52 void set_shift(double shift) { m_last_shift=shift; }
53 void set_tempo(double tempo) { m_last_tempo=tempo; }
54 void set_formant_shift(double shift)
58 WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *GetBuffer(int size)
60 return m_inbuf.Resize(size*m_last_nch);
62 void BufferDone(int input_filled);
63 void FlushSamples() {}
65 static const char *enumQual(int q);
66 static bool GetSizes(int qv, int *ws, int *os); // if ws or os is NULL, enumerate available ws/os
68 int GetSamples(int requested_output, WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *buffer);
71 void SetQualityParameter(int parm)
73 m_qual=parm;
76 #ifdef WDL_SIMPLEPITCHSHIFT_EXTRA_INTERFACE
77 WDL_SIMPLEPITCHSHIFT_EXTRA_INTERFACE
78 #endif
80 private:
81 void PitchShiftBlock(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *inputs, WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *outputs, int nch, int length, double pitch, int bsize, int olsize, double srate);
84 private:
85 double m_pspos WDL_FIXALIGN;
86 double m_tempo_fracpos;
87 double m_srate,m_last_tempo,m_last_shift;
89 WDL_TypedBuf<WDL_SIMPLEPITCHSHIFT_SAMPLETYPE> m_psbuf;
90 WDL_Queue m_queue;
91 WDL_TypedBuf<WDL_SIMPLEPITCHSHIFT_SAMPLETYPE> m_inbuf;
92 WDL_TypedBuf<WDL_SIMPLEPITCHSHIFT_SAMPLETYPE> m_rsbuf;
94 int m_pswritepos;
95 int m_last_nch;
96 int m_qual;
97 int m_hadinput;
99 int m_latpos;
104 #ifdef WDL_SIMPLEPITCHSHIFT_IMPLEMENT
105 void WDL_SimplePitchShifter::BufferDone(int input_filled)
107 // perform pitch shifting
108 if (input_filled>0)
110 m_hadinput=1;
111 int ws,os;
112 GetSizes(m_qual,&ws,&os);
114 int bsize=(int) (ws * 0.001 * m_srate);
115 if (bsize<16) bsize=16;
116 else if (bsize>128*1024)bsize=128*1024;
118 int olsize=(int) (os * 0.001 * m_srate);
119 if (olsize > bsize/2) olsize=bsize/2;
120 if (olsize<1)olsize=1;
121 if (m_psbuf.GetSize() != bsize*m_last_nch)
123 memset(m_psbuf.Resize(bsize*m_last_nch,false),0,sizeof(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE)*bsize*m_last_nch);
124 m_pspos=(double) (bsize/2);
125 m_pswritepos=0;
128 if (fabs(m_last_tempo-1.0)<0.0000000001)
130 PitchShiftBlock(m_inbuf.Get(),(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *)m_queue.Add(NULL,input_filled*m_last_nch*sizeof(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE)),m_last_nch,input_filled,m_last_shift,bsize,olsize,m_srate);
132 else
134 int needclear=m_rsbuf.GetSize()<m_last_nch;
136 WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *bufi=m_rsbuf.Resize((input_filled+1)*m_last_nch,false);
137 if (needclear)
138 memset(bufi,0,m_last_nch*sizeof(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE));
140 double tempo=m_last_tempo;
141 double itempo=1.0/tempo;
142 PitchShiftBlock(m_inbuf.Get(),bufi+m_last_nch,m_last_nch,input_filled,m_last_shift*itempo,bsize,olsize,m_srate);
144 double fp=m_tempo_fracpos;
147 if (m_latpos < bsize/2)
149 int a = bsize/2-m_latpos;
150 if (a > input_filled) a=input_filled;
151 m_latpos+=a;
152 fp += a;
155 int outlen = (int) (input_filled*itempo-fp) + 128; // upper bound on possible sample count
156 if (outlen<0)
158 outlen=0;
161 WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *bufo = (WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *)m_queue.Add(NULL,outlen*m_last_nch*sizeof(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE));
162 // resample bufi to bufo
163 int i,nch=m_last_nch;
164 for (i = 0; i < outlen; i ++)
166 double rdpos=floor(fp);
167 int idx=((int)rdpos);
168 if (idx>=input_filled)
170 // un-add any missing samples
171 m_queue.Add(NULL,(i-outlen)*m_last_nch*sizeof(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE));
172 break;
174 rdpos = (fp-rdpos);
175 int a;
176 idx*=nch;
177 for (a = 0; a < nch; a ++)
179 *bufo++ = bufi[idx+a]*(1.0-rdpos)+bufi[idx+nch+a]*rdpos;
181 fp += tempo;
184 memcpy(bufi,bufi+m_last_nch*input_filled,m_last_nch*sizeof(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE)); // save last sample for interpolation later
186 m_tempo_fracpos=fp-floor(fp);
191 const char *WDL_SimplePitchShifter::enumQual(int q)
193 int ws,os;
194 if (!GetSizes(q,&ws,&os)) return NULL;
195 static char buf[128];
196 sprintf(buf,"%dms window, %dms fade",ws,os);
197 return buf;
200 bool WDL_SimplePitchShifter::GetSizes(int qv, int *ws, int *os)
202 static const short windows[]={50,75,100,150,225,300,40,30,20,10,5,3};
203 static const char divs[]={2,3,5,7};
204 const int nwindows = (int) (sizeof(windows) / sizeof(windows[0]));
205 const int ndivs = (int) (sizeof(divs) / sizeof(divs[0]));
207 if (!os)
209 if (qv < 0 || qv >= nwindows) return false;
210 *ws = windows[qv];
211 return true;
213 if (!ws)
215 if (qv < 0 || qv >= ndivs) return false;
216 *os = divs[qv];
217 return true;
220 int wd=qv/ndivs;
221 if (wd >= nwindows) wd=-1;
223 *ws=windows[wd>=0?wd:0];
224 *os = *ws / divs[qv >= 0 ? qv%ndivs : 0];
225 if (*os<1) *os=1;
227 return wd>=0;
230 int WDL_SimplePitchShifter::GetSamples(int requested_output, WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *buffer)
232 if (!m_last_nch||requested_output<1) return 0;
234 int l=m_queue.Available()/sizeof(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE)/m_last_nch;
236 if (requested_output>l) requested_output=l;
237 int sz=requested_output*sizeof(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE)*m_last_nch;
238 memcpy(buffer,m_queue.Get(),sz);
239 m_queue.Advance(sz);
240 m_queue.Compact();
241 return requested_output;
244 void WDL_SimplePitchShifter::PitchShiftBlock(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *inputs, WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *outputs, int nch, int length, double pitch, int bsize, int olsize, double srate)
246 double iolsize=1.0/olsize;
248 WDL_SIMPLEPITCHSHIFT_SAMPLETYPE *psbuf=m_psbuf.Get();
250 double dpi=pitch;
252 double pspos=m_pspos;
253 int writepos=m_pswritepos;
254 int writeposnch = writepos*nch;
255 int bsizench = bsize*nch;
256 int olsizench = olsize*nch;
258 int i=length;
259 while (i--)
261 int ipos1=(int)pspos;
262 double frac0=pspos-ipos1;
264 ipos1*=nch;
266 int ipos2=ipos1+nch;
268 if (ipos2 >= bsizench) ipos2=0;
270 int a;
271 for(a=0;a<nch;a++) outputs[a]=(psbuf[ipos1+a]*(1-frac0)+psbuf[ipos2+a]*frac0);
273 double tv=pspos;
274 if (dpi >= 1.0)
276 if (tv > writepos) tv-=bsize;
278 if (tv >= writepos-olsize && tv < writepos)
280 double tfrac=(writepos-tv)*iolsize;
281 int tmp=ipos1+olsizench;
282 if (tmp>=bsizench) tmp-=bsizench;
283 int tmp2=tmp+nch;
284 if (tmp2 >= bsizench) tmp2=0;
286 for(a=0;a<nch;a++) outputs[a]= outputs[a]*tfrac + (1-tfrac)*(psbuf[tmp+a]*(1-frac0)+psbuf[tmp2+a]*frac0);
288 if (tv+pitch >= writepos) pspos+=olsize;
292 else
294 if (tv<writepos) tv+=bsize;
296 if (tv >= writepos && tv < writepos+olsize)
298 double tfrac=(tv-writepos)*iolsize;
299 int tmp=ipos1+olsizench;
300 if (tmp>=bsizench) tmp -= bsizench;
301 int tmp2= tmp+nch;
302 if (tmp2 >= bsizench) tmp2=0;
303 for(a=0;a<nch;a++) outputs[a] = outputs[a]*tfrac + (1-tfrac)*(psbuf[tmp+a]*(1-frac0)+psbuf[tmp2+a]*frac0);
305 // this is wrong, but blehhh?
306 if (tv+pitch < writepos+1) pspos += olsize;
307 // if (tv+pitch >= writepos+olsize) pspos += olsize;
312 if ((pspos+=pitch) >= bsize) pspos -= bsize;
315 memcpy(psbuf+writeposnch,inputs,nch*sizeof(WDL_SIMPLEPITCHSHIFT_SAMPLETYPE));
317 writeposnch += nch;
318 if (++writepos >= bsize) writeposnch = writepos=0;
320 inputs += nch;
321 outputs += nch;
322 } // sample loop
323 m_pspos=pspos;
324 m_pswritepos=writepos;
327 #endif
329 #endif