Mercurial > hg > x
view QuickSpec.cpp @ 1:6422640a802f
first upload
author | Wen X <xue.wen@elec.qmul.ac.uk> |
---|---|
date | Tue, 05 Oct 2010 10:45:57 +0100 |
parents | |
children |
line wrap: on
line source
//--------------------------------------------------------------------------- #include <math.h> #include <memory.h> #include "QuickSpec.h" //--------------------------------------------------------------------------- /* method TQuickSpectrogram::TQuickSpectrogram: In: AParent: pointer argument for calling G, if G is specified AnId: integer argument for calling G, if G is specified G: pointer to a function that supplies buffers used for FFT, 0 by default Ausex: set if complete complex spectrogram is to be buffered and accessible Auseph: set if phase spectrogram is to be buffered and accessible */ __fastcall TQuickSpectrogram::TQuickSpectrogram(void* AParent, int AnId, bool Ausex, bool Auseph, GetBuf G) { memset(this, 0, sizeof(TQuickSpectrogram)); Parent=AParent; Id=AnId; usex=Ausex; useph=Auseph; GetFFTBuffers=G; BufferSize=QSpec_BufferSize; fwt=wtRectangle; Wid=1024; Offst=512; }//TQuickSpectrogram //TQuickSpectrogram::~TQuickSpectrogram __fastcall TQuickSpectrogram::~TQuickSpectrogram() { FreeBuffers(); free8(fw); free8(fwin); free(fhbi); }//~TQuickSpectrogram //--------------------------------------------------------------------------- /* method TQuickSpectrogram::A: accesses amplitude spectrogram by frame In: fr: frame index, 0-based Returns pointer to amplitude spectrum of the fr'th frame, NULL if N/A */ QSPEC_FORMAT* __fastcall TQuickSpectrogram::A(int fr) { if (Capacity==0) SetFrCapacity((DataLength-Wid)/Offst+2); if (fr<0 || fr>=Capacity) return NULL; if (Frame[fr]<0 || !Valid[fr]) CalculateSpectrum(fr); return fA[Frame[fr]]; }//A //--------------------------------------------------------------------------- /* method TQuickSpectrogram::AddBuffer: increases internal buffer by BufferSize frames. Allocated buffers beyond Capacity frames will not be indexed or used by TQuickSpectrogram. */ void TQuickSpectrogram::AddBuffer() { int base=1, Dim=Wid/2+1; if (usex) base+=2; if (useph) base+=1; QSPEC_FORMAT* newbuffer=(QSPEC_FORMAT*)malloc(sizeof(QSPEC_FORMAT)*Dim*BufferSize*base); int fr0=BufferCount*BufferSize; for (int i=0; i<BufferSize; i++) { int fr=fr0+i; if (fr<Capacity) { fA[fr]=&newbuffer[i*Dim]; int base=1; if (usex) fSpec[fr]=(cmplx<QSPEC_FORMAT>*)&newbuffer[(BufferSize+i*2)*Dim], base+=2; if (useph) fPh[fr]=&newbuffer[(BufferSize*base+i)*Dim]; } else break; } BufferCount++; }//AddBuffer /* method TQuickSpectrogram::AddBuffer: increase internal buffer by a multiple of BufferSize so that it will be enough to host another AddFrCount frames. */ void TQuickSpectrogram::AddBuffer(int AddFrCount) { while (FrCount+AddFrCount>BufferSize*BufferCount) AddBuffer(); }//AddBuffer //--------------------------------------------------------------------------- /* function IntToDouble: copy content of integer array to double array In: in: pointer to integer array BytesPerSample: number of bytes each integer takes Count: size of integer array, in integers Out: vector out[Count]. No return value. */ void IntToDouble(double* out, void* in, int BytesPerSample, int Count) { if (BytesPerSample==1){unsigned char* in8=(unsigned char*)in; for (int k=0; k<Count; k++) *(out++)=*(in8++)-128.0;} else if (BytesPerSample==2) {__int16* in16=(__int16*)in; for (int k=0; k<Count; k++) *(out++)=*(in16++);} else {__pint24 in24=(__pint24)in; for (int k=0; k<Count; k++) *(out++)=*(in24++);} }//IntToDouble /* function CalculateSpectrum: calculate spectrum of a signal in integer format In: Data[Wid]: integer array hosting waveform data BytesPerSample: number of bytes each integer in Data[] takes win[Wid]: window function used for computing spectrum w[Wid/2], x[Wid], hbi[Wid/2]: FFT buffers Out: x[Wid]: complex spectrum Amp[Wid/2+1]: amplitude spectrum Arg[Wid/2+1]: phase spectrum, optional No return value. */ void CalculateSpectrum(void* Data, int BytesPerSample, double* win, QSPEC_FORMAT* Amp, QSPEC_FORMAT* Arg, int Wid, cdouble* w, cdouble* x, int* hbi) { if (BytesPerSample==2) RFFTCW((__int16*)Data, win, 0, 0, log2(Wid), w, x, hbi); else {IntToDouble((double*)x, Data, BytesPerSample, Wid); RFFTCW((double*)x, win, 0, 0, log2(Wid), w, x, hbi);} for (int j=0; j<=Wid/2; j++) { Amp[j]=sqrt(x[j].x*x[j].x+x[j].y*x[j].y); if (Arg) Arg[j]=(x[j].y==0 && x[j].x==0)?0:atan2(x[j].y, x[j].x); } }//CalculateSpectrum /* function CalculateSpectrum: calculate spectrum of a signal in integer format, allowing the signal length $eff be shorter than the DFT size Wid. In: Data[eff]: integer array hosting waveform data BytesPerSample: number of bytes each integer in Data[] takes win[Wid]: window function used for computing spectrum w[Wid/2], x[Wid], hbi[Wid/2]: FFT buffers Out: x[Wid]: complex spectrum Amp[Wid/2+1]: amplitude spectrum Arg[Wid/2+1]: phase spectrum, optional No return value. */ void CalculateSpectrum(void* Data, int BytesPerSample, double* win, QSPEC_FORMAT* Amp, QSPEC_FORMAT* Arg, int Wid, int eff, cdouble* w, cdouble* x, int* hbi) { if (eff<=0) { memset(Amp, 0, sizeof(double)*(Wid/2+1)); if (Arg) memset(Arg, 0, sizeof(double)*(Wid/2+1)); } else if (eff<Wid) { double* doublex=(double*)x; IntToDouble(doublex, Data, BytesPerSample, eff); memset(&doublex[eff], 0, sizeof(double)*(Wid-eff)); RFFTCW(doublex, win, 0, 0, log2(Wid), w, x, hbi); for (int j=0; j<=Wid/2; j++) { Amp[j]=sqrt(x[j].x*x[j].x+x[j].y*x[j].y); if (Arg) Arg[j]=(x[j].y==0 && x[j].x==0)?0:atan2(x[j].y, x[j].x); } } else CalculateSpectrum(Data, BytesPerSample, win, Amp, Arg, Wid, w, x, hbi); }//CalculateSpectrum /* method TQuickSpectrogram::CalculateSpectrum: computes spectrogram at fr'th frame. In: fr: index to the frame whose spectrum is to be computed. fr must be between 0 and Capacity-1. */ void __fastcall TQuickSpectrogram::CalculateSpectrum(int fr) { cdouble *w, *x; double* win; int* hbi; //obtain FFT buffers win (window function), w (twiddle factors), x (data buffer), //hbi (half-size bit-inversed integer table) if (GetFFTBuffers) //then use external buffers provided through GetFFTBuffers GetFFTBuffers(Id, w, x, win, hbi, Parent); else //then use internal buffers { if (Wid!=fWid) { //then update internal buffers to the new window size free8(fw); free(fhbi); fw=(cdouble*)malloc8(sizeof(cdouble)*Wid*1.5); SetTwiddleFactors(Wid, fw); fx=&fw[Wid/2]; fhbi=CreateBitInvTable(log2(Wid)-1); } if (Wid!=fWid || WinType!=fwt || WinParam!=fwdp) { //then update internal window function to the new window type fwin=NewWindow8(WinType, Wid, 0, &WinParam); fwt=WinType; fwdp=WinParam; } fWid=Wid; //pick up the internal buffers w=fw, x=fx, win=fwin, hbi=fhbi; } //obtain the index of this frame in internal storage if (Frame[fr]<0) {AddBuffer(1); Frame[fr]=FrCount; FrCount++;} int realfr=Frame[fr]; //obtain the pointer to this frame's phase spectrum QSPEC_FORMAT *lph=useph?fPh[realfr]:NULL; //ontain the pointer to this frame's complex spectrum cmplx<QSPEC_FORMAT>* lX=usex?fSpec[realfr]:NULL; //choose the buffer actually used for FFT - use lX if it is specified as complex double array //because it saves unnecessary data copying operations cdouble *lx=(usex && sizeof(QSPEC_FORMAT)==sizeof(double))?(cdouble*)lX:x; //Actual FFT if (fr*Offst+Wid<=DataLength) ::CalculateSpectrum(&((char*)Data)[fr*Offst*BytesPerSample], BytesPerSample, win, fA[realfr], lph, Wid, w, lx, hbi); else ::CalculateSpectrum(&((char*)Data)[fr*Offst*BytesPerSample], BytesPerSample, win, fA[realfr], lph, Wid, DataLength-fr*Offst, w, x, hbi); //optional data copy from x to lX if (usex && lx==x) for (int i=0; i<Wid/2+1; i++) lX[i]=x[i]; //tag this frame as computed and valid Valid[fr]=1; }//CalculateSpectrum //--------------------------------------------------------------------------- /* method TQuickSpectrogram::FreeBuffers: discards all computed spectra and free all internal buffers. This returns the TQuickSpectrogram to its initial state before any frame is accessed. After calling FreeBuffers() all frames will be recomputed when they are accessed. */ void TQuickSpectrogram::FreeBuffers() { if (fA) { for (int i=0; i<BufferCount; i++) free(fA[i*BufferSize]); FrCount=BufferCount=Capacity=0; free(Frame); free(Valid); free(fA); Frame=Valid=0, fA=0; if (useph) {free(fPh); fPh=0;} if (usex) {free(fSpec); fSpec=0;} } }//FreeBuffers //--------------------------------------------------------------------------- /* method TQuickSpectrogram::Invalidate: renders all frames that have overlap with interval [From, To], measured in samples, as invalid. Invalid frames are recomputed when they are accessed again. In: [From, To]: an interval spectrogram over which needs to be updated. Returns the number of allocated frames affected, no matter if they were valid. */ int TQuickSpectrogram::Invalidate(int From, int To) { int result=0; if (Frame) { int fr1=ceil((From-Wid+1.0)/Offst), fr2=floor(1.0*To/Offst); if (fr1<0) fr1=0; if (fr2>=Capacity) fr2=Capacity-1; for (int fr=fr1; fr<=fr2; fr++) if (Frame[fr]>=0) Valid[fr]=false, result++; } return result; }//Invalidate //--------------------------------------------------------------------------- /* method TQuickSpectrogram::Ph: accesses phase spectrogram by frame In: fr: frame index, 0-based Returns pointer to phase spectrum of the fr'th frame, NULL if N/A */ QSPEC_FORMAT* __fastcall TQuickSpectrogram::Ph(int fr) { if (Capacity==0) SetFrCapacity((DataLength-Wid)/Offst+2); if (fr<0 || fr>=Capacity) return NULL; if (Frame[fr]<0 || !Valid[fr]) CalculateSpectrum(fr); return fPh[Frame[fr]]; }//Ph //--------------------------------------------------------------------------- /* method TQuickSpectrogram::SetFrCapacity: sets the capacity, i.e. the maximal number of frames handled by this TQuickSpectrogram. In: AnFrCapacity: the new Capacity, in frames This method should not be called to set Capacity to a smaller value. */ void TQuickSpectrogram::SetFrCapacity(int AnFrCapacity) { //adjusting the size of index and validity arrays Frame=(int*)realloc(Frame, sizeof(int)*AnFrCapacity); Valid=(int*)realloc(Valid, sizeof(int)*AnFrCapacity); // fA=(QSPEC_FORMAT**)realloc(fA, sizeof(QSPEC_FORMAT*)*AnFrCapacity); if (usex) fSpec=(cmplx<QSPEC_FORMAT>**)realloc(fSpec, sizeof(cmplx<QSPEC_FORMAT>*)*AnFrCapacity); if (useph) fPh=(QSPEC_FORMAT**)realloc(fPh, sizeof(QSPEC_FORMAT*)*AnFrCapacity); if (AnFrCapacity>Capacity) { memset(&Frame[Capacity], 0xFF, sizeof(int)*(AnFrCapacity-Capacity)); memset(&Valid[Capacity], 0x00, sizeof(int)*(AnFrCapacity-Capacity)); if (Capacity<BufferCount*BufferSize) { for (int fr=Capacity; fr<AnFrCapacity; fr++) { int bufferno=fr/BufferSize; if (bufferno<BufferCount) { QSPEC_FORMAT* thisbuffer=fA[BufferSize*bufferno]; int lfr=fr%BufferSize, base=1, Dim=Wid/2+1; fA[fr]=&thisbuffer[lfr*Dim]; if (usex) fSpec[fr]=(cmplx<QSPEC_FORMAT>*)(&thisbuffer[(BufferSize+lfr*2)*Dim]), base+=2; if (useph) fPh[fr]=&thisbuffer[(BufferSize*base+lfr)*Dim]; } else break; } } } Capacity=AnFrCapacity; }//SetFrCapacity //--------------------------------------------------------------------------- /* method TQuickSpectrogram::Ph: accesses complex spectrogram by frame In: fr: frame index, 0-based Returns pointer to complex spectrum of the fr'th frame, NULL if N/A */ cmplx<QSPEC_FORMAT>* __fastcall TQuickSpectrogram::Spec(int fr) { if (Capacity==0) SetFrCapacity((DataLength-Wid)/Offst+2); if (fr<0 || fr>=Capacity) return NULL; if (Frame[fr]<0 || !Valid[fr]) CalculateSpectrum(fr); return fSpec[Frame[fr]]; }//Spec