comparison quickspec.cpp @ 2:fc19d45615d1

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