annotate quickspec.cpp @ 13:de3961f74f30 tip

Add Linux/gcc Makefile; build fix
author Chris Cannam
date Mon, 05 Sep 2011 15:22:35 +0100
parents 977f541d6683
children
rev   line source
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