⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sample.cpp

📁 C++ , microphone spectrogram viewer, real time
💻 CPP
📖 第 1 页 / 共 2 页
字号:
#include <vcl.h>
#include <mmsystem.h>
#include <math.h>
#include <dsgnintf.hpp>
#include <stdlib.h>
#pragma hdrstop
USERES("sample.res");
USEPACKAGE("vcl35.bpi");
USEPACKAGE("vclx35.bpi");
USERES("sample.dcr");
//---------------------------------------------------------------------------
#include "sample.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) {return 1;}
//---------------------------------------------------------------------------


__fastcall TWaveInDeviceID::TWaveInDeviceID() : TPersistent() {Fid=WAVE_MAPPER;}
__fastcall TWaveInDeviceID::~TWaveInDeviceID() {}
void __fastcall TWaveInDeviceID::Assign(TPersistent* Value) {id=dynamic_cast<TWaveInDeviceID*>(Value)->id;}
void __fastcall TWaveInDeviceID::Changed() {if (FOnChange!=NULL) FOnChange(this);}
void __fastcall TWaveInDeviceID::SetId(int Value) {if (Value!=Fid) {Fid=Value; Changed();}}

__fastcall TWaveInDeviceIDProperty::TWaveInDeviceIDProperty() : TOrdinalProperty() {deviceList=NULL; ndevs=0; }
__fastcall TWaveInDeviceIDProperty::~TWaveInDeviceIDProperty() {if (deviceList!=NULL) delete deviceList; deviceList=NULL; }
TPropertyAttributes __fastcall TWaveInDeviceIDProperty::GetAttributes() {TPropertyAttributes pa; pa << paMultiSelect << paValueList << paRevertable << paReadOnly; return pa; }
AnsiString __fastcall TWaveInDeviceIDProperty::GetValue()
{ GenerateDeviceList(); int l=GetOrdValue(); if ((l<0 || l>=ndevs) && (l!=WAVE_MAPPER)) l=WAVE_MAPPER;
  int i; if (l==WAVE_MAPPER) i=0; else i=l+1; return deviceList->Strings[i]; }
void __fastcall TWaveInDeviceIDProperty::GetValues(Classes::TGetStrProc Proc)
{ GenerateDeviceList(); for (int i=0; i<ndevs+1; i++) {Proc(deviceList->Strings[i]);}}
void __fastcall TWaveInDeviceIDProperty::SetValue(const AnsiString val)
{ AnsiString c=val; int pos=c.Pos(":"); if (pos!=0) c=c.SubString(1,pos-1);
  int ord=c.ToInt(); SetOrdValue(ord); }
void __fastcall TWaveInDeviceIDProperty::GenerateDeviceList()
{ if (deviceList!=NULL) return; deviceList=new TStringList(); ndevs=waveInGetNumDevs();
  for (int i=-1; i<ndevs; i++) {WAVEINCAPS wic; UINT dev=i; if (i==-1) i=WAVE_MAPPER;
    MMRESULT mres=waveInGetDevCaps(dev,&wic,sizeof(WAVEINCAPS));
    if (mres==MMSYSERR_NOERROR) { String s=String(i)+": "; if (i==-1) s=s+"[default] "; s=s+wic.szPname; deviceList->Add(s); }}}



__fastcall TAudioLineParameters::TAudioLineParameters() : TPersistent() {FChannels=1;FFrequency=11025;FBitsPerSample=8;}
__fastcall TAudioLineParameters::~TAudioLineParameters() {}
void __fastcall TAudioLineParameters::Changed() {if (FOnChange!=NULL) FOnChange(this);}
void __fastcall TAudioLineParameters::SetChannels(int Value) {if (Value!=FChannels) {FChannels=Value; Changed();}}
void __fastcall TAudioLineParameters::SetFrequency(int f) {if (f!=FFrequency) {FFrequency=f; Changed();}}
void __fastcall TAudioLineParameters::SetBitsPerSample(int bps) {if (bps!=FBitsPerSample) {FBitsPerSample=bps; Changed();}}
void __fastcall TAudioLineParameters::Assign(TPersistent *Value)
{ TAudioLineParameters *asp=dynamic_cast<TAudioLineParameters*>(Value); if (asp==NULL) return;
  Channels=asp->Channels; Frequency=asp->Frequency; BitsPerSample=asp->BitsPerSample; }


__fastcall TAudioLineParametersProperty::TAudioLineParametersProperty() : TClassProperty()
{ allowedList=NULL; nallowed=0; }
__fastcall TAudioLineParametersProperty::~TAudioLineParametersProperty() {DestroyAllowedList();}
void __fastcall TAudioLineParametersProperty::DestroyAllowedList()
{ if (allowedList!=NULL) delete allowedList; allowedList=NULL; nallowed=0; }
TPropertyAttributes __fastcall TAudioLineParametersProperty::GetAttributes()
{ TPropertyAttributes pa=TClassProperty::GetAttributes(); pa << paValueList; return pa; }
void __fastcall TAudioLineParametersProperty::GetValues(Classes::TGetStrProc Proc)
{ GenerateAllowedList(); for (int i=0; i<nallowed; i++) {Proc(allowedList->Strings[i]);}}
AnsiString __fastcall TAudioLineParametersProperty::GetValue()
{ TAudioLineParameters *asp=(TAudioLineParameters*)(GetOrdValue());
  if (asp==NULL) throw new EPropertyError("Audio stream parameters not found");
  String s=String(asp->Frequency)+"Hz, "; if (asp->Channels==1) s=s+"mono, ";
  else if (asp->Channels==2) s=s+"stereo, ";  else s=s+String(asp->Channels)+"channels, ";
  s=s+String(asp->BitsPerSample)+"bit"; return s; }
void __fastcall TAudioLineParametersProperty::SetValue(AnsiString v)
{ int i=v.Pos(","); if (i==0) throw new EPropertyError("Badly formed audio parameters");
  String freq=v.SubString(1,i-1).Trim().LowerCase(); v=v.SubString(i+1,v.Length()-i);
  i=v.Pos(","); if (i==0) throw new EPropertyError("Badly formed audio parameters");
  String channels=v.SubString(1,i-1).Trim().LowerCase(); String bits=v.SubString(i+1,v.Length()-i).Trim().LowerCase();
  i=freq.Pos("hz"); if (i!=0) freq=freq.SubString(1,i-1); int iFreq=freq.ToInt(); int iChannels;
  if (channels=="mono") iChannels=1; else if (channels=="stereo") iChannels=2; else iChannels=channels.ToInt();
  i=bits.Pos("bit"); if (i!=0) bits=bits.SubString(1,i-1); int iBits=bits.ToInt();
  TAudioLineParameters *asp=(TAudioLineParameters*)(GetOrdValue()); if (asp==NULL) throw new EPropertyError("Audio stream parameters not found");
  asp->Frequency=iFreq; asp->Channels=iChannels; asp->BitsPerSample=iBits; Modified(); }
void __fastcall TAudioLineParametersProperty::GenerateAllowedList()
{ if (allowedList!=NULL) return; allowedList=new TStringList(); nallowed=0;
  WAVEINCAPS wic; MMRESULT mres=waveInGetDevCaps(WAVE_MAPPER,&wic,sizeof(WAVEINCAPS));
  if (mres!=MMSYSERR_NOERROR) return; DWORD f=wic.dwFormats;
  if (f&WAVE_FORMAT_1M08) allowedList->Add("11025Hz, mono, 8bit");
  if (f&WAVE_FORMAT_1M16) allowedList->Add("11025Hz, mono, 16bit");
  if (f&WAVE_FORMAT_1S08) allowedList->Add("11025Hz, stereo, 8bit");
  if (f&WAVE_FORMAT_1S16) allowedList->Add("11025Hz, stereo, 16bit");
  if (f&WAVE_FORMAT_2M08) allowedList->Add("22050Hz, mono, 8bit");
  if (f&WAVE_FORMAT_2M16) allowedList->Add("22050Hz, mono, 16bit");
  if (f&WAVE_FORMAT_2S08) allowedList->Add("22050Hz, stereo, 8bit");
  if (f&WAVE_FORMAT_2S16) allowedList->Add("22050Hz, stereo, 16bit");
  if (f&WAVE_FORMAT_4M08) allowedList->Add("44100Hz, mono, 8bit");
  if (f&WAVE_FORMAT_4M16) allowedList->Add("44100Hz, mono, 16bit");
  if (f&WAVE_FORMAT_4S08) allowedList->Add("44100Hz, stereo, 8bit");
  if (f&WAVE_FORMAT_4S16) allowedList->Add("44100Hz, stereo, 16bit");
  nallowed=allowedList->Count; }





//---------------------------------------------------------------------------

// a number of channels
// NUM_BUF buffers. 8 of them, probably.
// Each buffer is _cbBuf big.
// A single sample is the reading at one instant.
// cSamplePerSec is the sampling frequency.
// cSamples is the number of samples that we'll do at a time.
// Thus, if cSamplePerSec==cSamples, each buffer will be one second long.
// A buffer might store several channels.

__fastcall TRecorder::TRecorder(TComponent *AOwner) : TComponent(AOwner)
{ hControlWnd=AllocateHWnd(this->ControlWndProc);
  FWaveInDevice=new TWaveInDeviceID();
  FQuality=new TAudioLineParameters();
  FAutoStart=false;
  //
  FSamplesPerBuffer=256;
  FOnSample=NULL;
  FHandle=NULL;
  //
  nextBuf=0;
  sizeSample=0;
  sizeBuf=0;
  numBufs=NUM_BUFS;
  pBuf=NULL;
  //
  for (int i=0; i<MAX_INTERESTS; i++) interest[i]=NULL;
  PostMessage(hControlWnd,WM_USER,0,0); // delayed action start
}


__fastcall TRecorder::~TRecorder()
{ for (int i=0; i<MAX_INTERESTS; i++) {if (interest[i]!=NULL) interest[i](this,rrTidy);}
  Stop(); DestroyHandle();
  if (hControlWnd!=NULL) DeallocateHWnd(hControlWnd); hControlWnd=NULL;
  if (FWaveInDevice!=NULL) delete FWaveInDevice; FWaveInDevice=NULL;
  if (FQuality!=NULL) delete FQuality; FQuality=NULL;
}

void __fastcall TRecorder::AddInterest(TRecorderNotification ie)
{ int ipos=-1;
  for (int i=0; i<MAX_INTERESTS; i++) if (interest[i]==NULL) ipos=i;
  if (ipos==-1) return;
  interest[ipos]=ie;
}
void __fastcall TRecorder::RemoveInterest(TRecorderNotification ie)
{ for (int i=0; i<MAX_INTERESTS; i++) if (interest[i]==ie) interest[i]=NULL;
}

int __fastcall TRecorder::GetNumDevices() {return (int)waveInGetNumDevs();}

void __fastcall TRecorder::SetWaveInDevice(TWaveInDeviceID *id) {if (FWaveInDevice!=NULL) delete FWaveInDevice; FWaveInDevice=id;}
void __fastcall TRecorder::SetQuality(TAudioLineParameters *asp) {if (FQuality!=NULL) delete FQuality; FQuality=asp;}
void __fastcall TRecorder::SetSamplesPerBuffer(int spb) {FSamplesPerBuffer=spb; DestroyHandle();}
HWAVEIN __fastcall TRecorder::GetHandle() {if (FHandle==NULL) CreateHandle(); return FHandle;}
void __fastcall TRecorder::SetHEvent(HANDLE h) {FHEvent=h; DestroyHandle();}
bool __fastcall TRecorder::GetIsNextBufferReady() {return (header[nextBuf].dwFlags & WHDR_DONE);}
unsigned int __fastcall TRecorder::GetSamplePos()
{ MMTIME mtime; mtime.wType = TIME_SAMPLES;
  waveInGetPosition(Handle,&mtime,sizeof(MMTIME)); return mtime.u.sample;
}
int __fastcall TRecorder::GetIData(int i)
{ if (Quality->Channels==1)
  { if (Quality->BitsPerSample==8) return ((unsigned char)bData[i]-128)*64;
    if (Quality->BitsPerSample==16) return ((short*)wData)[i];
  }
  if (Quality->Channels==2)
  { if (Quality->BitsPerSample==8) return ((unsigned char)bData[2*i]+(unsigned char)bData[2*i+1]-2*128)*128;
    if (Quality->BitsPerSample==16) return (((short*)wData)[2*i]+((short*)wData)[2*i+1])/2;
  }
  return bData[i];
}


void __fastcall TRecorder::CreateHandle()
{ if (FHandle!=NULL) return;
  sizeSample=Quality->Channels*Quality->BitsPerSample/8;
  sizeBuf=BufSize*Quality->Channels*Quality->BitsPerSample/8; // bytes per buffer
  pBuf=new char[sizeBuf*numBufs];
  // Thanks to Paul Reed for pointing out this problem
  WAVEFORMATEX format;
  format.wFormatTag=WAVE_FORMAT_PCM;
  format.nChannels=(WORD)(Quality->Channels);
  format.wBitsPerSample=(WORD)(Quality->BitsPerSample);
  format.nSamplesPerSec=Quality->Frequency;
  format.nBlockAlign=(WORD)(format.nChannels*format.wBitsPerSample/8);
  format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
  format.cbSize=0;
  MMRESULT mres=waveInOpen(0,0,&format,0,0,WAVE_FORMAT_QUERY);
  if (mres!=MMSYSERR_NOERROR)
  { throw new EInOutError("Format not supported");
  }
  DWORD dwCallback, fdwOpen;
  if (FHEvent!=NULL)
  { dwCallback=(DWORD)hEvent;
    fdwOpen=CALLBACK_EVENT;
  }
  else
  { if (hControlWnd==NULL) hControlWnd=AllocateHWnd(this->ControlWndProc);
    dwCallback=(DWORD)hControlWnd;
    fdwOpen=CALLBACK_WINDOW;
  }
  mres=waveInOpen(&FHandle,0,&format,dwCallback,0,fdwOpen);
  if (mres!=MMSYSERR_NOERROR)
  { throw new EInOutError("Error opening wavein");
  }
  // Don't initialize the last buffer. It will be initialized in the first call to BufferDone
  for (int i=0; i<numBufs-1; i++)
  { header[i].lpData=&pBuf[i*sizeBuf];
    header[i].dwBufferLength=sizeBuf;
    header[i].dwFlags=0;
    header[i].dwLoops=0;
    waveInPrepareHeader(Handle,&header[i],sizeof(WAVEHDR));
    waveInAddBuffer(Handle,&header[i],sizeof(WAVEHDR));
  }
}
void __fastcall TRecorder::DestroyHandle()
{ if (FHandle==NULL) return;
  waveInReset(FHandle);
  waveInClose(FHandle);
  FHandle=NULL;
  if (pBuf!=NULL) delete[] pBuf; pBuf=NULL;
}

void __fastcall TRecorder::Start()
{ nextBuf=0;
  waveInStart(Handle);
  FIsStarted=true;
}
void __fastcall TRecorder::Stop()
{ waveInStop(Handle);
  FIsStarted=false;
}
void __fastcall TRecorder::Reset()
{ waveInReset(Handle);
  FIsStarted=false;
}


void __fastcall TRecorder::ReadyForNextBuffer()
{ if (!isNextBufferReady) return;
  int justDoneBuf=(nextBuf-1+numBufs)%numBufs;
  int nowWorkingOnBuf=nextBuf;
  nextBuf=(nextBuf+1)%numBufs;
  waveInUnprepareHeader(Handle,&header[nowWorkingOnBuf],sizeof(WAVEHDR));
  header[justDoneBuf].lpData=&pBuf[justDoneBuf*sizeBuf];
  header[justDoneBuf].dwBufferLength=sizeBuf;
  header[justDoneBuf].dwFlags=0;
  header[justDoneBuf].dwLoops=0;
  waveInPrepareHeader(Handle,&header[justDoneBuf],sizeof(WAVEHDR));
  waveInAddBuffer(Handle,&header[justDoneBuf],sizeof(WAVEHDR));
  // So, that has dispatched the one that the user was previously working on
  // nextBuf is the one that the computer is currently filling
  // and we can now manipulate the data in nextBuf.
  bData=(LPBYTE)header[nowWorkingOnBuf].lpData;
  wData=(LPWORD)header[nowWorkingOnBuf].lpData;
  BufNumBytes=header[nowWorkingOnBuf].dwBytesRecorded;
  BufNumSamples=BufNumBytes*8/(Quality->Channels*Quality->BitsPerSample);
}

void __fastcall TRecorder::ControlWndProc(TMessage &Message)
{ if (Message.Msg==MM_WIM_DATA)
  { // Message.WParam will be Handle, and LParam will be ptr to wavehdr
    ReadyForNextBuffer(); // to say that the one we were working on previously has been finished with
    // How do we know it's finished? Because, in this single-threaded program, the only
    // sensible way we could have got here is if it was.
    // (Note: if the user did something silly in their event, like running a message loop,
    // then we might end up being called here again. That's their fault).
    for (int i=0; i<MAX_INTERESTS; i++) {if (interest[i]!=NULL) interest[i](this,rrData); }
    if (FOnSample!=NULL) FOnSample(this);
  }
  if (Message.Msg==MM_WIM_OPEN)
  { for (int i=0; i<MAX_INTERESTS; i++) {if (interest[i]!=NULL) interest[i](this,rrOpen); }
  }
  if (Message.Msg==MM_WIM_CLOSE)
  { for (int i=0; i<MAX_INTERESTS; i++) {if (interest[i]!=NULL) interest[i](this,rrClose); }
  }
  if (Message.Msg==WM_USER)
  { if (AutoStart && !ComponentState.Contains(csDesigning)) Start();
  }
}


bool __fastcall TRecorder::IsThisQualityPossible()
{ return IsQualityPossible(Quality->Frequency,Quality->Channels,Quality->BitsPerSample);
}
bool __fastcall TRecorder::IsQualityPossible(int freq,int channels,int bits)
{ WAVEFORMATEX wfx;
  wfx.wFormatTag=WAVE_FORMAT_PCM;
  wfx.nChannels=(WORD)channels;
  wfx.nSamplesPerSec=freq;
  wfx.nAvgBytesPerSec=freq*channels*bits/8;
  wfx.wBitsPerSample=(WORD)bits;
  wfx.cbSize=0;
  MMRESULT mres=waveInOpen(NULL,Device->id,&wfx,0,0,WAVE_FORMAT_QUERY);
  return (mres==MMSYSERR_NOERROR);
}

















#define PI 3.14159265358979323846274383278
//---------------------------------------------------------------------------
__fastcall TFFT::TFFT(TComponent* Owner)
        : TComponent(Owner)
{ isRegistered=false; FSource=NULL;
  FnPoints=1024;
  freq=0;
  aTape=NULL;
  aBitRev=NULL;
  W=NULL;
  X=NULL;
  //
  for (int i=0; i<MAX_FFTINTERESTS; i++) interest[i]=NULL;
}
__fastcall TFFT::~TFFT()
{ UnregisterInterest();
  for (int i=0; i<MAX_FFTINTERESTS; i++) {if (interest[i]!=NULL) interest[i](this,rrTidy);}
  CloseSettings();
}
void __fastcall TFFT::CloseSettings()
{ if (aTape!=NULL) delete[] aTape; aTape=NULL;
  if (aBitRev!=NULL) delete[] aBitRev; aBitRev=NULL;
  if (W!=NULL)
  { for (int l=1; l<=logPoints; l++)
    { if (W[l]!=NULL) delete[] W[l]; }
    delete[] W; W=NULL;
  }
  if (X!=NULL) delete[] X; X=NULL;
}

void __fastcall TFFT::AddInterest(TRecorderNotification ie)
{ int ipos=-1;
  for (int i=0; i<MAX_FFTINTERESTS; i++) if (interest[i]==NULL) ipos=i;
  if (ipos==-1) return;
  interest[ipos]=ie;
}
void __fastcall TFFT::RemoveInterest(TRecorderNotification ie)
{ for (int i=0; i<MAX_FFTINTERESTS; i++) if (interest[i]==ie) interest[i]=NULL;
}

void __fastcall TFFT::SetSource(TRecorder *s)
{ if (FSource==s) return;
  UnregisterInterest();
  FSource=s;
  freq=FSource->Quality->Frequency;
  RegisterInterest();
  AdjustNPoints();
  s->FreeNotification(this);
  //UpdateSettings(); happens when it stream gets opened
}
void __fastcall TFFT::Notification(TComponent *c,TOperation op)
{ if (op==opRemove && c==FSource) FSource=NULL; CloseSettings();
}

void __fastcall TFFT::UnregisterInterest()
{ if (FSource!=NULL && isRegistered) FSource->RemoveInterest(InterestProc);
  isRegistered=false;
}
void __fastcall TFFT::RegisterInterest()
{ if (FSource==NULL || isRegistered) return;
  FSource->AddInterest(InterestProc); isRegistered=true;
}
void __fastcall TFFT::InterestProc(TObject *Sender, TRecorderReason rr)
{ if (rr==rrTidy) {isRegistered=false; return;}
  if (rr==rrData) Sample(Source);
  if (rr==rrOpen) UpdateSettings();
}

int __fastcall TFFT::GetData(int i) {return (int) aTape[i];};
double __fastcall TFFT::IntensityFromFrequency(int i) {return X[i].Mod()/sqrtPoints;}
int __fastcall TFFT::FrequencyFromPoint(int point) {long x=freq*point; return x/FnPoints;}
int __fastcall TFFT::PointFromFrequency(int afreq) {return (long)FnPoints*afreq/freq;}
int __fastcall TFFT::MaxFreqency() {return freq;}

void __fastcall TFFT::SetNPoints(int v)
{ FnPoints=v; bool existedbefore=(aTape!=NULL);
  CloseSettings(); if (existedbefore) UpdateSettings(); else AdjustNPoints();
}
void __fastcall TFFT::AdjustNPoints()
{ int maxsource=0; if (FSource!=NULL) maxsource=FSource->BufSize;//*2;
  int maxcur=FnPoints;
  int max=maxcur; if (maxsource>max) max=maxsource;
  int i=1; while (i<max) i=i*2; FnPoints=i;
}

void __fastcall TFFT::UpdateSettings()
{ if (Source==NULL) return;
  AdjustNPoints();
  //
  aTape = new double [FnPoints];
  for (int i=0;i<FnPoints; i++) aTape[i] = 0;
  sqrtPoints = sqrt((double)FnPoints);
  // calculate binary log
  int tempPoints=FnPoints;
  logPoints=0; tempPoints--;
  while (tempPoints!=0) {tempPoints >>= 1; logPoints++;}
  aBitRev=new int [FnPoints];
  X=new TComplex[FnPoints];
  W=new TComplex*[logPoints+1];

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -