xue@11
|
1 /*
|
xue@11
|
2 Harmonic sinusoidal modelling and tools
|
xue@11
|
3
|
xue@11
|
4 C++ code package for harmonic sinusoidal modelling and relevant signal processing.
|
xue@11
|
5 Centre for Digital Music, Queen Mary, University of London.
|
xue@11
|
6 This file copyright 2011 Wen Xue.
|
xue@11
|
7
|
xue@11
|
8 This program is free software; you can redistribute it and/or
|
xue@11
|
9 modify it under the terms of the GNU General Public License as
|
xue@11
|
10 published by the Free Software Foundation; either version 2 of the
|
xue@11
|
11 License, or (at your option) any later version.
|
xue@11
|
12 */
|
xue@1
|
13 //---------------------------------------------------------------------------
|
xue@1
|
14 #include <math.h>
|
xue@1
|
15 #include <memory.h>
|
Chris@2
|
16 #include "quickspec.h"
|
xue@1
|
17
|
Chris@5
|
18 /** \file quickspec.h */
|
xue@1
|
19
|
xue@1
|
20 //---------------------------------------------------------------------------
|
Chris@5
|
21 /**
|
xue@1
|
22 method TQuickSpectrogram::TQuickSpectrogram:
|
xue@1
|
23
|
xue@1
|
24 In: AParent: pointer argument for calling G, if G is specified
|
xue@1
|
25 AnId: integer argument for calling G, if G is specified
|
xue@1
|
26 G: pointer to a function that supplies buffers used for FFT, 0 by default
|
xue@1
|
27 Ausex: set if complete complex spectrogram is to be buffered and accessible
|
xue@1
|
28 Auseph: set if phase spectrogram is to be buffered and accessible
|
xue@1
|
29 */
|
xue@1
|
30 __fastcall TQuickSpectrogram::TQuickSpectrogram(void* AParent, int AnId, bool Ausex, bool Auseph, GetBuf G)
|
xue@1
|
31 {
|
xue@1
|
32 memset(this, 0, sizeof(TQuickSpectrogram));
|
xue@1
|
33 Parent=AParent;
|
xue@1
|
34 Id=AnId;
|
xue@1
|
35 usex=Ausex;
|
xue@1
|
36 useph=Auseph;
|
xue@1
|
37 GetFFTBuffers=G;
|
xue@1
|
38 BufferSize=QSpec_BufferSize;
|
xue@1
|
39 fwt=wtRectangle;
|
xue@1
|
40 Wid=1024;
|
xue@1
|
41 Offst=512;
|
xue@1
|
42 }//TQuickSpectrogram
|
xue@1
|
43
|
xue@1
|
44 //TQuickSpectrogram::~TQuickSpectrogram
|
xue@1
|
45 __fastcall TQuickSpectrogram::~TQuickSpectrogram()
|
xue@1
|
46 {
|
xue@1
|
47 FreeBuffers();
|
xue@1
|
48 free8(fw);
|
xue@1
|
49 free8(fwin);
|
xue@1
|
50 free(fhbi);
|
xue@1
|
51 }//~TQuickSpectrogram
|
xue@1
|
52
|
xue@1
|
53 //---------------------------------------------------------------------------
|
Chris@5
|
54 /**
|
xue@1
|
55 method TQuickSpectrogram::A: accesses amplitude spectrogram by frame
|
xue@1
|
56
|
xue@1
|
57 In: fr: frame index, 0-based
|
xue@1
|
58
|
xue@1
|
59 Returns pointer to amplitude spectrum of the fr'th frame, NULL if N/A
|
xue@1
|
60 */
|
xue@1
|
61 QSPEC_FORMAT* __fastcall TQuickSpectrogram::A(int fr)
|
xue@1
|
62 {
|
xue@1
|
63 if (Capacity==0) SetFrCapacity((DataLength-Wid)/Offst+2);
|
xue@1
|
64 if (fr<0 || fr>=Capacity) return NULL;
|
xue@1
|
65 if (Frame[fr]<0 || !Valid[fr]) CalculateSpectrum(fr);
|
xue@1
|
66 return fA[Frame[fr]];
|
xue@1
|
67 }//A
|
xue@1
|
68
|
xue@1
|
69 //---------------------------------------------------------------------------
|
Chris@5
|
70 /**
|
xue@1
|
71 method TQuickSpectrogram::AddBuffer: increases internal buffer by BufferSize frames. Allocated buffers
|
xue@1
|
72 beyond Capacity frames will not be indexed or used by TQuickSpectrogram.
|
xue@1
|
73 */
|
xue@1
|
74 void TQuickSpectrogram::AddBuffer()
|
xue@1
|
75 {
|
xue@1
|
76 int base=1, Dim=Wid/2+1;
|
xue@1
|
77 if (usex) base+=2;
|
xue@1
|
78 if (useph) base+=1;
|
xue@1
|
79 QSPEC_FORMAT* newbuffer=(QSPEC_FORMAT*)malloc(sizeof(QSPEC_FORMAT)*Dim*BufferSize*base);
|
xue@1
|
80 int fr0=BufferCount*BufferSize;
|
xue@1
|
81 for (int i=0; i<BufferSize; i++)
|
xue@1
|
82 {
|
xue@1
|
83 int fr=fr0+i;
|
xue@1
|
84 if (fr<Capacity)
|
xue@1
|
85 {
|
xue@1
|
86 fA[fr]=&newbuffer[i*Dim];
|
xue@1
|
87 int base=1;
|
xue@1
|
88 if (usex) fSpec[fr]=(cmplx<QSPEC_FORMAT>*)&newbuffer[(BufferSize+i*2)*Dim], base+=2;
|
xue@1
|
89 if (useph) fPh[fr]=&newbuffer[(BufferSize*base+i)*Dim];
|
xue@1
|
90 }
|
xue@1
|
91 else break;
|
xue@1
|
92 }
|
xue@1
|
93 BufferCount++;
|
xue@1
|
94 }//AddBuffer
|
xue@1
|
95
|
Chris@5
|
96 /**
|
xue@1
|
97 method TQuickSpectrogram::AddBuffer: increase internal buffer by a multiple of BufferSize so that
|
xue@1
|
98 it will be enough to host another AddFrCount frames.
|
xue@1
|
99 */
|
xue@1
|
100 void TQuickSpectrogram::AddBuffer(int AddFrCount)
|
xue@1
|
101 {
|
xue@1
|
102 while (FrCount+AddFrCount>BufferSize*BufferCount) AddBuffer();
|
xue@1
|
103 }//AddBuffer
|
xue@1
|
104
|
xue@1
|
105 //---------------------------------------------------------------------------
|
Chris@5
|
106 /**
|
xue@1
|
107 function IntToDouble: copy content of integer array to double array
|
xue@1
|
108
|
xue@1
|
109 In: in: pointer to integer array
|
xue@1
|
110 BytesPerSample: number of bytes each integer takes
|
xue@1
|
111 Count: size of integer array, in integers
|
xue@1
|
112 Out: vector out[Count].
|
xue@1
|
113
|
xue@1
|
114 No return value.
|
xue@1
|
115 */
|
xue@1
|
116 void IntToDouble(double* out, void* in, int BytesPerSample, int Count)
|
xue@1
|
117 {
|
xue@1
|
118 if (BytesPerSample==1){unsigned char* in8=(unsigned char*)in; for (int k=0; k<Count; k++) *(out++)=*(in8++)-128.0;}
|
xue@1
|
119 else if (BytesPerSample==2) {__int16* in16=(__int16*)in; for (int k=0; k<Count; k++) *(out++)=*(in16++);}
|
xue@1
|
120 else {__pint24 in24=(__pint24)in; for (int k=0; k<Count; k++) *(out++)=*(in24++);}
|
xue@1
|
121 }//IntToDouble
|
xue@1
|
122
|
Chris@5
|
123 /**
|
xue@1
|
124 function CalculateSpectrum: calculate spectrum of a signal in integer format
|
xue@1
|
125
|
xue@1
|
126 In: Data[Wid]: integer array hosting waveform data
|
xue@1
|
127 BytesPerSample: number of bytes each integer in Data[] takes
|
xue@1
|
128 win[Wid]: window function used for computing spectrum
|
xue@1
|
129 w[Wid/2], x[Wid], hbi[Wid/2]: FFT buffers
|
xue@1
|
130 Out: x[Wid]: complex spectrum
|
xue@1
|
131 Amp[Wid/2+1]: amplitude spectrum
|
xue@1
|
132 Arg[Wid/2+1]: phase spectrum, optional
|
xue@1
|
133
|
xue@1
|
134 No return value.
|
xue@1
|
135 */
|
xue@1
|
136 void CalculateSpectrum(void* Data, int BytesPerSample, double* win, QSPEC_FORMAT* Amp, QSPEC_FORMAT* Arg, int Wid, cdouble* w, cdouble* x, int* hbi)
|
xue@1
|
137 {
|
xue@11
|
138 if (BytesPerSample==2) RFFTCW((__int16*)Data, win, 0, 0, Log2(Wid), w, x, hbi);
|
xue@11
|
139 else {IntToDouble((double*)x, Data, BytesPerSample, Wid); RFFTCW((double*)x, win, 0, 0, Log2(Wid), w, x, hbi);}
|
xue@1
|
140 for (int j=0; j<=Wid/2; j++)
|
xue@1
|
141 {
|
xue@1
|
142 Amp[j]=sqrt(x[j].x*x[j].x+x[j].y*x[j].y);
|
xue@1
|
143 if (Arg) Arg[j]=(x[j].y==0 && x[j].x==0)?0:atan2(x[j].y, x[j].x);
|
xue@1
|
144 }
|
xue@1
|
145 }//CalculateSpectrum
|
xue@1
|
146
|
Chris@5
|
147 /**
|
xue@1
|
148 function CalculateSpectrum: calculate spectrum of a signal in integer format, allowing the signal
|
xue@1
|
149 length $eff be shorter than the DFT size Wid.
|
xue@1
|
150
|
xue@1
|
151 In: Data[eff]: integer array hosting waveform data
|
xue@1
|
152 BytesPerSample: number of bytes each integer in Data[] takes
|
xue@1
|
153 win[Wid]: window function used for computing spectrum
|
xue@1
|
154 w[Wid/2], x[Wid], hbi[Wid/2]: FFT buffers
|
xue@1
|
155 Out: x[Wid]: complex spectrum
|
xue@1
|
156 Amp[Wid/2+1]: amplitude spectrum
|
xue@1
|
157 Arg[Wid/2+1]: phase spectrum, optional
|
xue@1
|
158
|
xue@1
|
159 No return value.
|
xue@1
|
160 */
|
xue@1
|
161 void CalculateSpectrum(void* Data, int BytesPerSample, double* win, QSPEC_FORMAT* Amp, QSPEC_FORMAT* Arg, int Wid, int eff, cdouble* w, cdouble* x, int* hbi)
|
xue@1
|
162 {
|
xue@1
|
163 if (eff<=0)
|
xue@1
|
164 {
|
xue@1
|
165 memset(Amp, 0, sizeof(double)*(Wid/2+1));
|
xue@1
|
166 if (Arg) memset(Arg, 0, sizeof(double)*(Wid/2+1));
|
xue@1
|
167 }
|
xue@1
|
168 else if (eff<Wid)
|
xue@1
|
169 {
|
xue@1
|
170 double* doublex=(double*)x;
|
xue@1
|
171 IntToDouble(doublex, Data, BytesPerSample, eff); memset(&doublex[eff], 0, sizeof(double)*(Wid-eff));
|
xue@11
|
172 RFFTCW(doublex, win, 0, 0, Log2(Wid), w, x, hbi);
|
xue@1
|
173 for (int j=0; j<=Wid/2; j++)
|
xue@1
|
174 {
|
xue@1
|
175 Amp[j]=sqrt(x[j].x*x[j].x+x[j].y*x[j].y);
|
xue@1
|
176 if (Arg) Arg[j]=(x[j].y==0 && x[j].x==0)?0:atan2(x[j].y, x[j].x);
|
xue@1
|
177 }
|
xue@1
|
178 }
|
xue@1
|
179 else
|
xue@1
|
180 CalculateSpectrum(Data, BytesPerSample, win, Amp, Arg, Wid, w, x, hbi);
|
xue@1
|
181 }//CalculateSpectrum
|
xue@1
|
182
|
Chris@5
|
183 /**
|
xue@1
|
184 method TQuickSpectrogram::CalculateSpectrum: computes spectrogram at fr'th frame.
|
xue@1
|
185
|
xue@1
|
186 In: fr: index to the frame whose spectrum is to be computed. fr must be between 0 and Capacity-1.
|
xue@1
|
187 */
|
xue@1
|
188 void __fastcall TQuickSpectrogram::CalculateSpectrum(int fr)
|
xue@1
|
189 {
|
xue@1
|
190 cdouble *w, *x;
|
xue@1
|
191 double* win;
|
xue@1
|
192 int* hbi;
|
xue@1
|
193
|
xue@1
|
194 //obtain FFT buffers win (window function), w (twiddle factors), x (data buffer),
|
xue@1
|
195 //hbi (half-size bit-inversed integer table)
|
xue@1
|
196 if (GetFFTBuffers) //then use external buffers provided through GetFFTBuffers
|
xue@1
|
197 GetFFTBuffers(Id, w, x, win, hbi, Parent);
|
xue@1
|
198 else //then use internal buffers
|
xue@1
|
199 {
|
xue@1
|
200 if (Wid!=fWid)
|
xue@1
|
201 { //then update internal buffers to the new window size
|
xue@1
|
202 free8(fw); free(fhbi);
|
xue@1
|
203 fw=(cdouble*)malloc8(sizeof(cdouble)*Wid*1.5); SetTwiddleFactors(Wid, fw);
|
xue@1
|
204 fx=&fw[Wid/2];
|
xue@11
|
205 fhbi=CreateBitInvTable(Log2(Wid)-1);
|
xue@1
|
206 }
|
xue@1
|
207 if (Wid!=fWid || WinType!=fwt || WinParam!=fwdp)
|
xue@1
|
208 { //then update internal window function to the new window type
|
xue@1
|
209 fwin=NewWindow8(WinType, Wid, 0, &WinParam);
|
xue@1
|
210 fwt=WinType; fwdp=WinParam;
|
xue@1
|
211 }
|
xue@1
|
212 fWid=Wid;
|
xue@1
|
213
|
xue@1
|
214 //pick up the internal buffers
|
xue@1
|
215 w=fw, x=fx, win=fwin, hbi=fhbi;
|
xue@1
|
216 }
|
xue@1
|
217
|
xue@1
|
218 //obtain the index of this frame in internal storage
|
xue@1
|
219 if (Frame[fr]<0) {AddBuffer(1); Frame[fr]=FrCount; FrCount++;}
|
xue@1
|
220 int realfr=Frame[fr];
|
xue@1
|
221
|
xue@1
|
222 //obtain the pointer to this frame's phase spectrum
|
xue@1
|
223 QSPEC_FORMAT *lph=useph?fPh[realfr]:NULL;
|
xue@1
|
224 //ontain the pointer to this frame's complex spectrum
|
xue@1
|
225 cmplx<QSPEC_FORMAT>* lX=usex?fSpec[realfr]:NULL;
|
xue@1
|
226 //choose the buffer actually used for FFT - use lX if it is specified as complex double array
|
xue@1
|
227 //because it saves unnecessary data copying operations
|
xue@1
|
228 cdouble *lx=(usex && sizeof(QSPEC_FORMAT)==sizeof(double))?(cdouble*)lX:x;
|
xue@1
|
229
|
xue@1
|
230 //Actual FFT
|
xue@1
|
231 if (fr*Offst+Wid<=DataLength)
|
xue@1
|
232 ::CalculateSpectrum(&((char*)Data)[fr*Offst*BytesPerSample], BytesPerSample, win, fA[realfr], lph, Wid, w, lx, hbi);
|
xue@1
|
233 else
|
xue@1
|
234 ::CalculateSpectrum(&((char*)Data)[fr*Offst*BytesPerSample], BytesPerSample, win, fA[realfr], lph, Wid, DataLength-fr*Offst, w, x, hbi);
|
xue@1
|
235
|
xue@1
|
236 //optional data copy from x to lX
|
xue@1
|
237 if (usex && lx==x) for (int i=0; i<Wid/2+1; i++) lX[i]=x[i];
|
xue@1
|
238
|
xue@1
|
239 //tag this frame as computed and valid
|
xue@1
|
240 Valid[fr]=1;
|
xue@1
|
241 }//CalculateSpectrum
|
xue@1
|
242
|
xue@1
|
243 //---------------------------------------------------------------------------
|
Chris@5
|
244 /**
|
xue@1
|
245 method TQuickSpectrogram::FreeBuffers: discards all computed spectra and free all internal buffers.
|
xue@1
|
246 This returns the TQuickSpectrogram to its initial state before any frame is accessed. After calling
|
xue@1
|
247 FreeBuffers() all frames will be recomputed when they are accessed.
|
xue@1
|
248 */
|
xue@1
|
249 void TQuickSpectrogram::FreeBuffers()
|
xue@1
|
250 {
|
xue@1
|
251 if (fA)
|
xue@1
|
252 {
|
xue@1
|
253 for (int i=0; i<BufferCount; i++) free(fA[i*BufferSize]);
|
xue@1
|
254 FrCount=BufferCount=Capacity=0;
|
xue@1
|
255 free(Frame); free(Valid);
|
xue@1
|
256 free(fA);
|
xue@1
|
257 Frame=Valid=0, fA=0;
|
xue@1
|
258 if (useph) {free(fPh); fPh=0;}
|
xue@1
|
259 if (usex) {free(fSpec); fSpec=0;}
|
xue@1
|
260 }
|
xue@1
|
261 }//FreeBuffers
|
xue@1
|
262
|
xue@1
|
263 //---------------------------------------------------------------------------
|
Chris@5
|
264 /**
|
xue@1
|
265 method TQuickSpectrogram::Invalidate: renders all frames that have overlap with interval [From, To],
|
xue@1
|
266 measured in samples, as invalid. Invalid frames are recomputed when they are accessed again.
|
xue@1
|
267
|
xue@1
|
268 In: [From, To]: an interval spectrogram over which needs to be updated.
|
xue@1
|
269
|
xue@1
|
270 Returns the number of allocated frames affected, no matter if they were valid.
|
xue@1
|
271 */
|
xue@1
|
272 int TQuickSpectrogram::Invalidate(int From, int To)
|
xue@1
|
273 {
|
xue@1
|
274 int result=0;
|
xue@1
|
275 if (Frame)
|
xue@1
|
276 {
|
xue@1
|
277 int fr1=ceil((From-Wid+1.0)/Offst), fr2=floor(1.0*To/Offst);
|
xue@1
|
278 if (fr1<0) fr1=0;
|
xue@1
|
279 if (fr2>=Capacity) fr2=Capacity-1;
|
xue@1
|
280 for (int fr=fr1; fr<=fr2; fr++) if (Frame[fr]>=0) Valid[fr]=false, result++;
|
xue@1
|
281 }
|
xue@1
|
282 return result;
|
xue@1
|
283 }//Invalidate
|
xue@1
|
284
|
xue@1
|
285 //---------------------------------------------------------------------------
|
Chris@5
|
286 /**
|
xue@1
|
287 method TQuickSpectrogram::Ph: accesses phase spectrogram by frame
|
xue@1
|
288
|
xue@1
|
289 In: fr: frame index, 0-based
|
xue@1
|
290
|
xue@1
|
291 Returns pointer to phase spectrum of the fr'th frame, NULL if N/A
|
xue@1
|
292 */
|
xue@1
|
293 QSPEC_FORMAT* __fastcall TQuickSpectrogram::Ph(int fr)
|
xue@1
|
294 {
|
xue@1
|
295 if (Capacity==0) SetFrCapacity((DataLength-Wid)/Offst+2);
|
xue@1
|
296 if (fr<0 || fr>=Capacity) return NULL;
|
xue@1
|
297 if (Frame[fr]<0 || !Valid[fr]) CalculateSpectrum(fr);
|
xue@1
|
298 return fPh[Frame[fr]];
|
xue@1
|
299 }//Ph
|
xue@1
|
300
|
xue@1
|
301 //---------------------------------------------------------------------------
|
Chris@5
|
302 /**
|
xue@1
|
303 method TQuickSpectrogram::SetFrCapacity: sets the capacity, i.e. the maximal number of frames handled
|
xue@1
|
304 by this TQuickSpectrogram.
|
xue@1
|
305
|
xue@1
|
306 In: AnFrCapacity: the new Capacity, in frames
|
xue@1
|
307
|
xue@1
|
308 This method should not be called to set Capacity to a smaller value.
|
xue@1
|
309 */
|
xue@1
|
310 void TQuickSpectrogram::SetFrCapacity(int AnFrCapacity)
|
xue@1
|
311 {
|
xue@1
|
312 //adjusting the size of index and validity arrays
|
xue@1
|
313 Frame=(int*)realloc(Frame, sizeof(int)*AnFrCapacity);
|
xue@1
|
314 Valid=(int*)realloc(Valid, sizeof(int)*AnFrCapacity);
|
xue@1
|
315
|
xue@1
|
316 //
|
xue@1
|
317 fA=(QSPEC_FORMAT**)realloc(fA, sizeof(QSPEC_FORMAT*)*AnFrCapacity);
|
xue@1
|
318 if (usex) fSpec=(cmplx<QSPEC_FORMAT>**)realloc(fSpec, sizeof(cmplx<QSPEC_FORMAT>*)*AnFrCapacity);
|
xue@1
|
319 if (useph) fPh=(QSPEC_FORMAT**)realloc(fPh, sizeof(QSPEC_FORMAT*)*AnFrCapacity);
|
xue@1
|
320 if (AnFrCapacity>Capacity)
|
xue@1
|
321 {
|
xue@1
|
322 memset(&Frame[Capacity], 0xFF, sizeof(int)*(AnFrCapacity-Capacity));
|
xue@1
|
323 memset(&Valid[Capacity], 0x00, sizeof(int)*(AnFrCapacity-Capacity));
|
xue@1
|
324
|
xue@1
|
325 if (Capacity<BufferCount*BufferSize)
|
xue@1
|
326 {
|
xue@1
|
327 for (int fr=Capacity; fr<AnFrCapacity; fr++)
|
xue@1
|
328 {
|
xue@1
|
329 int bufferno=fr/BufferSize;
|
xue@1
|
330 if (bufferno<BufferCount)
|
xue@1
|
331 {
|
xue@1
|
332 QSPEC_FORMAT* thisbuffer=fA[BufferSize*bufferno];
|
xue@1
|
333 int lfr=fr%BufferSize, base=1, Dim=Wid/2+1;
|
xue@1
|
334 fA[fr]=&thisbuffer[lfr*Dim];
|
xue@1
|
335 if (usex) fSpec[fr]=(cmplx<QSPEC_FORMAT>*)(&thisbuffer[(BufferSize+lfr*2)*Dim]), base+=2;
|
xue@1
|
336 if (useph) fPh[fr]=&thisbuffer[(BufferSize*base+lfr)*Dim];
|
xue@1
|
337 }
|
xue@1
|
338 else break;
|
xue@1
|
339 }
|
xue@1
|
340 }
|
xue@1
|
341 }
|
xue@1
|
342 Capacity=AnFrCapacity;
|
xue@1
|
343 }//SetFrCapacity
|
xue@1
|
344
|
xue@1
|
345 //---------------------------------------------------------------------------
|
Chris@5
|
346 /**
|
xue@1
|
347 method TQuickSpectrogram::Ph: accesses complex spectrogram by frame
|
xue@1
|
348
|
xue@1
|
349 In: fr: frame index, 0-based
|
xue@1
|
350
|
xue@1
|
351 Returns pointer to complex spectrum of the fr'th frame, NULL if N/A
|
xue@1
|
352 */
|
xue@1
|
353 cmplx<QSPEC_FORMAT>* __fastcall TQuickSpectrogram::Spec(int fr)
|
xue@1
|
354 {
|
xue@1
|
355 if (Capacity==0) SetFrCapacity((DataLength-Wid)/Offst+2);
|
xue@1
|
356 if (fr<0 || fr>=Capacity) return NULL;
|
xue@1
|
357 if (Frame[fr]<0 || !Valid[fr]) CalculateSpectrum(fr);
|
xue@1
|
358 return fSpec[Frame[fr]];
|
xue@1
|
359 }//Spec
|
xue@1
|
360
|
xue@1
|
361
|