📄 sample.cpp
字号:
#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 + -