diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/quickspec.cpp	Tue Oct 05 11:04:40 2010 +0100
@@ -0,0 +1,349 @@
+//---------------------------------------------------------------------------
+
+#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
+
+