MMFIR.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:53k
- {========================================================================}
- {= (c) 1995-98 SwiftSoft Ronald Dittrich =}
- {========================================================================}
- {= All Rights Reserved =}
- {========================================================================}
- {= D 01099 Dresden = Fax.: +49 (0)351-8037944 =}
- {= Loewenstr.7a = info@swiftsoft.de =}
- {========================================================================}
- {= Actual versions on http://www.swiftsoft.de/mmtools.html =}
- {========================================================================}
- {= This code is for reference purposes only and may not be copied or =}
- {= distributed in any format electronic or otherwise except one copy =}
- {= for backup purposes. =}
- {= =}
- {= No Delphi Component Kit or Component individually or in a collection=}
- {= subclassed or otherwise from the code in this unit, or associated =}
- {= .pas, .dfm, .dcu, .asm or .obj files may be sold or distributed =}
- {= without express permission from SwiftSoft. =}
- {= =}
- {= For more licence informations please refer to the associated =}
- {= HelpFile. =}
- {========================================================================}
- {= $Date: 09.09.98 - 12:05:10 $ =}
- {========================================================================}
- unit MMFIR;
- {$I COMPILER.INC}
- interface
- uses
- {$IFDEF WIN32}
- Windows,
- {$ELSE}
- WinTypes,
- WinProcs,
- {$ENDIF}
- SysUtils,
- Messages,
- Classes,
- Graphics,
- Controls,
- Forms,
- Dialogs,
- ExtCtrls,
- Menus,
- MMSystem,
- MMObj,
- MMDSPObj,
- MMObjLst,
- MMRegs,
- MMPCMSup,
- MMWaveIO,
- MMUtils,
- MMMuldiv,
- MMMath,
- MMFFT,
- MMSpectr,
- MMFIRFlt;
- const
- {$IFDEF CBUILDER3} {$EXTERNALSYM MAXFREQS} {$ENDIF}
- MAXFREQS = 256;
- {$IFDEF CBUILDER3} {$EXTERNALSYM MAXTAPS} {$ENDIF}
- MAXTAPS = 400;
- const
- {$IFDEF CBUILDER3} {$EXTERNALSYM defEnabled} {$ENDIF}
- defEnabled = True;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defColor} {$ENDIF}
- defColor = clWhite;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defGridColor} {$ENDIF}
- defGridColor = clGray;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defAxisColor} {$ENDIF}
- defAxisColor = clBlack;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defRespColor} {$ENDIF}
- defRespColor = clRed;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defCoeffColor} {$ENDIF}
- defCoeffColor = clBlue;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defScaleColor} {$ENDIF}
- defScaleColor = clBlack;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defDBScale} {$ENDIF}
- defDBScale = True;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defNormalized} {$ENDIF}
- defNormalized = False;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defShowCoeffs} {$ENDIF}
- defShowCoeffs = False;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defChannel} {$ENDIF}
- defChannel = chBoth;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defOrder} {$ENDIF}
- defOrder = 21;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defRate} {$ENDIF}
- defRate = 4000;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defWindow} {$ENDIF}
- defWindow = fwHamming;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defWidth} {$ENDIF}
- defWidth = 300;
- {$IFDEF CBUILDER3} {$EXTERNALSYM defHeight} {$ENDIF}
- defHeight = 300;
- type
- TMMFIRFilter = class;
- TMMFIRResponse = class;
- EMMFIRError = class(Exception);
- {-- TMMFIRFilterItem ------------------------------------------------------}
- TMMFIRFilterItem = class(TObject)
- private
- Ff1 : Float;
- FGain : Float;
- FOnChange: TNotifyEvent;
- procedure SetValue(index: integer; aValue: Float);
- procedure Store(S: TStream); virtual;
- procedure Load(S: TStream); virtual;
- protected
- procedure Changed; virtual;
- public
- constructor Create;
- constructor CreateEx(af1,aGain: Float);
- procedure Assign(Source: TObject);
- procedure SetParams(af1, aGain: Float);
- property OnChange: TNotifyEvent read FOnChange write FOnChange;
- property f1: Float index 0 read Ff1 write SetValue;
- property Gain: Float index 1 read FGain write SetValue;
- end;
- {-- TMMFIRFilterList ------------------------------------------------------}
- TMMFIRFilterList = class(TObjectList)
- private
- FFIRFilter: TMMFIRFilter;
- procedure SetFilter(Index: integer; Filter: TMMFIRFilterItem);
- function GetFilter(Index: integer): TMMFIRFilterItem;
- protected
- procedure DefineProperties(Filer: TFiler); override;
- procedure ReadData(S: TStream); override;
- procedure WriteData(S: TStream); override;
- public
- function AddObject(Item: TObject): TOLSize; override;
- procedure Sort;
- procedure Assign(Source: TPersistent); override;
- property Items[Index: integer]: TMMFIRFilterItem read GetFilter write SetFilter; default;
- end;
- {-- TMMFIRFilter ----------------------------------------------------------}
- TMMFIRFilter = class(TMMDSPComponent)
- private
- FOpen : Boolean;
- FEnabled : Boolean;
- FUpdating : Boolean;
- FDescription : String;
- Fncoeffs : integer;
- Fcoeffs : array[0..MAXTAPS-1] of Float;
- Ffs : Longint;
- FFilters : TMMFIRFilterList;
- FWindow : TMMFFTWindow;
- FCleanup : Longint;
- FPFIR : PFIRFilter;
- FPTempFIR : PFIRFilter;
- FChannel : TMMChannel;
- FTempBuffer : PChar;
- FResponse : TMMFIRResponse;
- FSpectrum : TMMSpectrum;
- FOnChange : TNotifyEvent;
- FOnPcmOverflow : TNotifyEvent;
- procedure SetWindow(aValue: TMMFFTWindow);
- procedure SetChannel(aValue: TMMChannel);
- procedure SetSampleRate(aValue: Longint);
- procedure SetNCoeffs(aValue: integer);
- procedure SetDescription(aValue: String);
- procedure SetFilters(aValue: TMMFIRFilterList);
- procedure SetResponse(aValue: TMMFIRResponse);
- procedure SetSpectrum(aValue: TMMSpectrum);
- procedure NotifyResponse(Operation: TOperation);
- procedure NotifySpectrum;
- procedure SpectrumNeedData(Sender: TObject);
- procedure FiltersChanged(Sender: TObject);
- procedure FilterChanged(Sender: TObject);
- procedure UpdateTempFilter(Init: Boolean);
- procedure CalcFilter;
- procedure UpdateFilter;
- protected
- procedure Change; virtual;
- procedure Loaded; override;
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- procedure SetPWaveFormat(aValue: PWaveFormatEx); override;
- procedure Assign(Source: TPersistent); override;
- procedure Opened; override;
- procedure Started; override;
- procedure Closed; override;
- procedure PcmOverflow; dynamic;
- procedure BufferReady(lpwh: PWaveHdr); override;
- procedure BufferLoad(lpwh: PWaveHdr; var MoreBuffers: Boolean); override;
- public
- constructor Create(aOwner: TComponent); override;
- destructor Destroy; override;
- procedure Open;
- procedure Start;
- procedure Close;
- procedure Process(Buffer: PChar; nBytes: Longint);
- function CleanUp(Buffer: PChar; Length: integer): Longint;
- procedure SetCoeffs(pCoeffs: PFloatArray; nCoeffs: integer);
- function GetCoeffs: PFloatArray;
- function LoadCoeffs(FName: TFileName): Boolean;
- function SaveCoeffs(FName: TFileName): Boolean;
- procedure SaveToIniFile(IniFile: TFileName; Section: string);
- procedure ReadFromIniFile(IniFile: TFileName; Section: string);
- published
- property OnChange: TNotifyEvent read FOnChange write FOnChange;
- property OnPcmOverflow: TNotifyEvent read FOnPcmOverflow write FOnPcmOverflow;
- property Input;
- property Output;
- property Enabled: Boolean read FEnabled write FEnabled default defEnabled;
- property SampleRate: Longint read Ffs write SetSampleRate default defRate;
- property Order: integer read FnCoeffs write SetNCoeffs default defOrder;
- property Description: String read FDescription write SetDescription stored False;
- property Filters: TMMFIRFilterList read FFilters write SetFilters;
- property Response: TMMFIRResponse read FResponse write SetResponse;
- property Spectrum: TMMSpectrum read FSpectrum write SetSpectrum;
- property Channel: TMMChannel read FChannel write SetChannel default defChannel;
- property Window: TMMFFTWindow read FWindow write SetWindow default defWindow;
- end;
- {-- TMMFIRResponse --------------------------------------------------------}
- TMMFIRResponse = class(TMMGraphicControl)
- private
- FClient : TRect;
- FDBScale : Boolean;
- FNormalized: Boolean;
- FShowCoeffs: Boolean;
- FGridColor : TColor;
- FAxisColor : TColor;
- FRespColor : TColor;
- FCoeffColor: TColor;
- FScaleColor: TColor;
- FnCoeffs : integer;
- FCoeffs : array[0..MAXTAPS-1] of Float;
- Ffs : Longint;
- procedure SetColors(index: integer; aValue: TColor);
- procedure SetBoolean(index: integer; aValue: Boolean);
- procedure SetSampleRate(aValue: Longint);
- procedure AdjustClientSize;
- procedure VLineDoted(aCanvas: TCanvas; x, y1, y2: integer; Clr: TColorRef);
- procedure HLineDoted(aCanvas: TCanvas; x1, x2, y: integer; Clr: TColorRef);
- procedure DrawBackground(Canvas: TCanvas; Client: TRect);
- procedure DrawImpulseResponse(Canvas: TCanvas; Client: TRect);
- protected
- procedure Paint; override;
- procedure Changed; override;
- procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); override;
- public
- constructor Create(aOwner: TComponent); override;
- destructor Destroy; override;
- procedure SetCoeffs(pCoeffs: PFloatArray; nCoeffs: integer);
- published
- property Color default defColor;
- property ParentColor;
- property Align;
- property Bevel;
- property Font;
- property ParentFont;
- property Visible;
- property Width default defWidth;
- property Height default defHeight;
- property GridColor: TColor index 0 read FGridColor write SetColors default defGridColor;
- property AxisColor: TColor index 1 read FAxisColor write SetColors default defAxisColor;
- property ResponseColor: TColor index 2 read FRespColor write SetColors default defRespColor;
- property CoeffColor: TColor index 3 read FCoeffColor write SetColors default defCoeffColor;
- property ScaleColor: TColor index 4 read FScaleColor write SetColors default defScaleColor;
- property ShowCoeffs: Boolean index 0 read FShowCoeffs write SetBoolean default defShowCoeffs;
- property DBScale: Boolean index 1 read FDBScale write SetBoolean default defDBScale;
- property Normalized: Boolean index 2 read FNormalized write SetBoolean default defNormalized;
- property SampleRate: Longint read Ffs write SetSampleRate default defRate;
- end;
- {-- Coeff generation and response --}
- type
- TMMFIRFunction = (ffLowPass,ffHighPass,ffBandPass,ffBandStop);
- PMMFilterPoint = ^TMMFilterPoint;
- TMMFilterPoint = record
- Freq: Float;
- Amp : Float;
- end;
- PMMFilterPoints = ^TMMFilterPoints;
- TMMFilterPoints = array[0..0] of TMMFilterPoint;
- procedure FIR_Coeffs(FIRType: TMMFIRFunction; SRate: integer;
- f1,f2: Float; N: integer; hh: PFloatArray);
- procedure FIR_CoeffsEx(Points: PMMFilterPoints; nPoints, SRate, N: integer;
- hh: PFloatArray);
- procedure FIR_Window(Window: TMMFFTWindow; N: integer; hh: PFloatArray);
- procedure FIR_Response (FirType,N: integer; hh: PFloatArray; dBScale: Boolean;
- numPoints: integer; points: PFloatArray);
- procedure FIR_NormaliseResponse(dbscale: Boolean; NumPoints: integer; points: PFloatArray);
- {$O-}
- implementation
- uses IniFiles;
- const
- STREAMKENNUNG : Longint = $00524946; { 'FIR ' }
- {==============================================================================}
- { note: delay occurs by ~ncoeff/2 samples }
- {==============================================================================}
- procedure FIR_Filter(pcoeffs: PFloatArray; ncoeffs: integer;
- DLine: PLongArray; Data: PSmallArray; samples: integer);
- var
- i,j,ki: integer;
- y: Float;
- idx: integer;
- mask: integer;
- begin
- mask := MAXTAPS-1;
- idx := MAXTAPS-nCoeffs;
- for i := 0 to samples-1 do
- begin
- ki := idx;
- DLine[idx] := Data[i];
- idx := (idx+1) and mask;
- y := 0.0;
- for j := 0 to ncoeffs-1 do
- begin
- ki := (ki-1) and mask;
- y := y + DLine[ki] * pcoeffs[j];
- end;
- Data[i] := MinMax(Trunc(y),-32767,32767);
- end;
- end;
- {==============================================================================}
- { interpolate to a y_value for a x_val using a table of points for x and y }
- { returns the y_val or FALSE if error, extrapolates if x_val is outside data }
- function FIR_Interpolate(ind_x, dep_y: PFloatArray; nPnts: integer;
- x_val: Float; var y_val: Float): Boolean;
- var
- i: integer;
- xrange: Float;
- begin
- Result := False;
- if (nPnts <= 0) then exit;
- if (nPnts = 1) then
- begin
- y_val := dep_y[0];
- Result := True;
- exit;
- end;
- i := 0;
- while (i < nPnts) and (x_val > ind_x[i]) do inc(i); // get to pair
- if (i = nPnts) then // extrapolate at end
- begin
- dec(i);
- xrange := ind_x[i] - ind_x[i-1];
- if (xrange = 0) then exit;
- y_val := dep_y[i]+(x_val-ind_x[i])*(dep_y[i]-dep_y[i-1])/xrange;
- Result := True;
- exit;
- end;
- if (x_val = ind_x[i]) then
- begin
- y_val := dep_y[i];
- Result := True;
- exit;
- end;
- if (i = 0) then // extrapolate at begining
- begin
- xrange := ind_x[i+1] - ind_x[i];
- if (xrange = 0) then exit;
- y_val := dep_y[i] - (ind_x[i]-x_val)*(dep_y[i+1]-dep_y[i]) /xrange;
- Result := True;
- exit;
- end;
- xrange := ind_x[i] - ind_x[i-1];
- if (xrange = 0) then exit;
- y_val := dep_y[i-1]+(x_val-ind_x[i-1])*(dep_y[i]-dep_y[i-1])/xrange;
- Result := True;
- end;
- {==============================================================================}
- procedure FIR_CoeffsEx(Points: PMMFilterPoints; nPoints, SRate,
- N: integer; hh: PFloatArray);
- var
- xt, q: Float;
- m, i, j: integer;
- freq,amp,dnpi: array [0..MAXFREQS-1] of Float;
- a: array [0..MAXTAPS-1] of Float;
- begin
- if (nPoints > MAXFREQS) then nPoints := MAXFREQS;
- for i := 0 to nPoints-1 do
- begin
- freq[i] := Points[i].Freq/SRate;
- amp[i] := pow(10.0,Points[i].Amp/20.0);
- end;
- m := (N + 1) div 2;
- q := 2 * M_PI / N;
- for i := 0 to nPoints-1 do // find positions of freqs in window
- begin
- dnpi[i] := N * freq[i] + 1.0;
- if (dnpi[i] < 0) then dnpi[i] := 0;
- if (dnpi[i] > N) then dnpi[i] := N;
- end;
- // set a[] array to the amp[] vals at each npi[] position
- for j := 0 to m do
- begin
- FIR_Interpolate(@dnpi,@amp,nPoints,j,a[j]);
- end;
- for i := 1 to m do // Calculate the coefficient array
- begin
- xt := a[1] / 2.0;
- for j := 2 to m do
- xt := xt + a[j] * cos(q*((m-i)*(j-1)));
- hh[i-1] := 2.0 * xt / N;
- end;
- for i := m+1 to n do // copy first n/2 coeff into last n/2 coeff
- hh[i-1] := hh[2*m-i-1];
- end;
- {==============================================================================}
- procedure FIR_Coeffs(FIRType: TMMFIRFunction; SRate: integer; f1,f2: Float;
- N: integer; hh: PFloatArray);
- var
- k: integer;
- mm,a: Float;
- fg,fg2: Float;
- begin
- case FIRType of
- ffLowpass:
- begin
- fg := f1/SRate;
- { build lowpass }
- for k := 0 to N-1 do
- begin
- mm := k-(N-1)/2.0;
- if (mm = 0) then
- hh[k] := (fg*2*M_PI)/M_PI
- else
- hh[k] := sin(mm*fg*2*M_PI)/(mm*M_PI);
- end;
- end;
- ffHighpass:
- begin
- fg := f1/SRate;
- { subtract a low pass from allpass }
- for k := 0 to N-1 do
- begin
- { simulate a allpass }
- if k = N div 2 then
- a := 1.0
- else
- a := 0.0;
- { the lowpass }
- mm := k-(N-1)/2.0;
- if (mm = 0) then
- hh[k] := a-((fg*2*M_PI)/M_PI)
- else
- hh[k] := a-(sin(mm*fg*2*M_PI)/(mm*M_PI));
- end;
- end;
- ffBandpass:
- begin
- fg := f1/SRate;
- fg2 := f2/SRate;
- { subtract a low pass from a low pass }
- for k := 0 to N-1 do
- begin
- mm := k-(N-1)/2.0;
- if (mm = 0) then
- hh[k] := ((fg2*2*M_PI)/M_PI)-((fg*2*M_PI)/M_PI)
- else
- hh[k] := (sin(mm*fg2*2*M_PI)/(mm*M_PI))-(sin(mm*fg*2*M_PI)/(mm*M_PI));
- end;
- end;
- ffBandstop:
- begin
- fg := f1/SRate;
- fg2:= f2/SRate;
- { build a bandpass and subtract it from a allpass }
- for k := 0 to N-1 do
- begin
- { simulate a allpass }
- if k = N div 2 then
- a := 1.0
- else
- a := 0.0;
- mm := k-(N-1)/2.0;
- if (mm = 0) then
- hh[k] := a-((fg2*2*M_PI)/M_PI)-((fg*2*M_PI)/M_PI)
- else
- hh[k] := a-(sin(mm*fg2*2*M_PI)/(mm*M_PI))-(sin(mm*fg*2*M_PI)/(mm*M_PI));
- end;
- end;
- end;
- end;
- {==============================================================================}
- procedure FIR_Window(Window: TMMFFTWindow; N: integer; hh: PFloatArray);
- const
- alpha = 5.0; { Gaussian window parameter }
- var
- i,N2: integer;
- function CalcWindow(idx: integer): Float;
- begin
- case ord(Window) of
- { Hamming }
- 1: Result := 0.54+0.46*cos(2*M_PI*idx/N);
- { Hanning }
- 2: Result := 0.5+0.5*cos(2*M_PI*idx/N);
- { Blackman }
- 3: Result := 0.42+0.5*cos(2*M_PI*idx/N)+0.08*cos(4*M_PI*idx/N);
- { Gaussian }
- 4: Result := exp( -alpha/(N*N) * (2*(N2-idx)-N)*(2*(N2-idx)-N) );
- { Welch }
- 5: Result := ((2*idx-N)/N)*((2*idx-N)/(N+1));
- { Parzen }
- 6: Result := abs((2*idx-N)/(N+1));
- { Rectangular }
- else Result := 1;
- end;
- end;
- begin
- N2 := N div 2;
- for i := 0 to N2 do
- begin
- hh[N2+i] := hh[N2+i] * CalcWindow(i);
- hh[N2-i] := hh[N2+i];
- end;
- end;
- {==============================================================================}
- procedure FIR_Response(FirType,N:integer; hh: PFloatArray; dBScale: Boolean;
- numPoints: integer; points: PFloatArray);
- var
- index, L, i: integer;
- lambda, work: Float;
- begin
- for L := 0 to NumPoints-1 do
- begin
- lambda := L*PI/NumPoints;
- case FirType of
- 1: begin
- work := hh[(N-1)div 2];
- for i := 1 to ((N-1)div 2) do
- begin
- index := (N-1)div 2-i;
- work := work+2.0*hh[index]*cos(i*lambda);
- end;
- end;
- 2: begin
- work := 0;
- for i := 1 to (N div 2) do
- begin
- index := N div 2-i;
- work := work+2.0*hh[index]*cos((i-0.05)*lambda);
- end;
- end;
- end;
- if dbscale then
- points[L] := 20.0*log10(abs(work))
- else
- points[L] := abs(work);
- end;
- end;
- {==============================================================================}
- procedure FIR_NormaliseResponse(dbscale: Boolean; NumPoints: integer; points: PFloatArray);
- var
- i: integer;
- biggest: Float;
- begin
- if dbscale then
- begin
- biggest := -100.0;
- for i := 0 to NumPoints-1 do
- if (points[i] > biggest) then biggest := points[i];
- for i := 0 to NumPoints-1 do
- points[i] := ((points[i]-biggest)/100)+1;
- end
- else
- begin
- biggest := 0.0;
- for i := 0 to NumPoints-1 do
- if (points[i] > biggest) then biggest := points[i];
- for i := 0 to NumPoints-1 do
- points[i] := points[i]/biggest;
- end;
- end;
- {== TMMFIRFilterItem ==========================================================}
- constructor TMMFIRFilterItem.Create;
- begin
- inherited Create;
- Ff1 := 0;
- FGain := 0;
- FOnChange := nil;
- end;
- {-- TMMFIRFilterItem ----------------------------------------------------------}
- constructor TMMFIRFilterItem.CreateEx(af1,aGain: Float);
- begin
- inherited Create;
- Ff1 := af1;
- FGain := aGain;
- FOnChange := nil;
- end;
- {-- TMMFIRFilterItem ----------------------------------------------------------}
- procedure TMMFIRFilterItem.Changed;
- begin
- if assigned(FOnChange) then FOnChange(Self);
- end;
- {-- TMMFIRFilterItem ----------------------------------------------------------}
- procedure TMMFIRFilterItem.SetParams(af1, aGain: Float);
- begin
- if (af1 <> Ff1) or (aGain <> FGain) then
- begin
- Ff1 := af1;
- FGain := aGain;
- Changed;
- end;
- end;
- {-- TMMFIRFilterItem ----------------------------------------------------------}
- procedure TMMFIRFilterItem.SetValue(index: integer; aValue: Float);
- var
- af1,aGain: Float;
- begin
- af1 := Ff1;
- aGain := FGain;
- case index of
- 0: af1 := aValue;
- 1: aGain := aValue;
- end;
- SetParams(af1, aGain);
- end;
- {-- TMMFIRFilterItem ----------------------------------------------------------}
- procedure TMMFIRFilterItem.Store(S: TStream);
- begin
- S.WriteBuffer(Ff1,SizeOf(Ff1));
- S.WriteBuffer(FGain,SizeOf(FGain));
- end;
- {-- TMMFIRFilterItem ----------------------------------------------------------}
- procedure TMMFIRFilterItem.Load(S: TStream);
- var
- af1,aGain: Float;
- begin
- S.ReadBuffer(af1,SizeOf(af1));
- S.ReadBuffer(aGain,SizeOf(aGain));
- SetParams(af1,aGain);
- end;
- {-- TMMFIRFilterItem ----------------------------------------------------------}
- procedure TMMFIRFilterItem.Assign(Source: TObject);
- begin
- if Source is TMMFIRFilterItem then
- begin
- SetParams(TMMFIRFilterItem(Source).f1,
- TMMFIRFilterItem(Source).Gain);
- end;
- end;
- {-- TMMFIRFilterList ----------------------------------------------------------}
- procedure TMMFIRFilterList.Sort;
- var
- i,j,h: integer;
- flt: TMMFIRFilterItem;
- begin { Start Shell-Sort }
- h := 1;
- while h <= Count div 9 do h := h*3 + 1;
- while h > 0 do
- begin
- for i := h to Count-1 do
- begin
- flt := Items[i];
- j := i;
- while (j >= h) and (Items[j-h].f1 > flt.f1) do
- begin
- Items[j] := Items[j-h];
- dec(j, h);
- end;
- Items[j] := flt;
- end;
- h := h div 3;
- end;
- end;
- {-- TMMFIRFilterList ----------------------------------------------------------}
- procedure TMMFIRFilterList.SetFilter(Index: integer; Filter: TMMFIRFilterItem);
- begin
- Put(Index, Filter);
- end;
- {-- TMMFIRFilterList ----------------------------------------------------------}
- function TMMFIRFilterList.GetFilter(Index: integer): TMMFIRFilterItem;
- begin
- Result := TMMFIRFilterItem(Get(Index));
- end;
- {-- TMMFIRFilterList ----------------------------------------------------------}
- function TMMFIRFilterList.AddObject(Item: TObject): TOLSize;
- begin
- Result := inherited AddObject(Item);
- (Item as TMMFIRFilterItem).OnChange := FFIRFilter.FilterChanged;
- end;
- {-- TMMFIRFilterList ----------------------------------------------------------}
- procedure TMMFIRFilterList.Assign(Source: TPersistent);
- var
- i: integer;
- Filter: TMMFIRFilterItem;
- begin
- if (Source is TMMFIRFilterList) or (Source = nil) then
- begin
- BeginUpdate;
- try
- if (FFIRFilter <> nil) then
- FFIRFilter.FUpdating := True;
- FreeAll;
- if (Source <> nil) then
- begin
- for i := 0 to TMMFIRFilterList(Source).Count-1 do
- begin
- Filter := TMMFIRFilterItem.Create;
- Filter.Assign(TMMFIRFilterList(Source)[i]);
- AddObject(Filter);
- end;
- end;
- finally
- if (FFIRFilter <> nil) then
- FFIRFilter.FUpdating := False;
- EndUpdate;
- end;
- end
- else inherited assign(Source);
- end;
- {-- TMMFIRFilterList ----------------------------------------------------------}
- procedure TMMFIRFilterList.DefineProperties(Filer: TFiler);
- begin
- Filer.DefineBinaryProperty('ObjectContainer', ReadData, WriteData, True);
- end;
- {-- TMMFIRFilterList ----------------------------------------------------------}
- procedure TMMFIRFilterList.ReadData(S: TStream);
- Var
- pBuf: PChar;
- Kennung: Longint;
- ObjCount,
- Index: TOLSize;
- Destroy: Boolean;
- Value: Longint;
- begin
- BeginUpdate;
- try
- FFIRFilter.FUpdating := True;
- S.ReadBuffer(Kennung,sizeOf(STREAMKENNUNG));
- if (Kennung <> STREAMKENNUNG) then
- raise EStreamError.Create('Invalid Object stream');
- FreeAll;
- { load stream items }
- S.ReadBuffer(Destroy,SizeOf(Destroy));
- DestroyObjects := Destroy;
- { read string length }
- S.ReadBuffer(Value,SizeOf(Value));
- if Value > 0 then
- begin
- pBuf := StrAlloc(Value+1);
- try
- FillChar(pBuf^, Value+1, 0);
- S.ReadBuffer(pBuf^, Value);
- FFIRFilter.Description := StrPas(pBuf);
- finally
- StrDispose(pBuf);
- end;
- end;
- S.ReadBuffer(ObjCount,SizeOf(Objcount)); { Read in Object count }
- if Capacity-Count < ObjCount then Capacity := Count+ObjCount;
- { Read in Object Count }
- for Index := 0 to ObjCount-1 do
- AddObject(ReadObjectFromStream(S));
- finally
- FFIRFilter.FUpdating := False;
- EndUpdate;
- end;
- end;
- {-- TMMFIRFilterList ----------------------------------------------------------}
- procedure TMMFIRFilterList.WriteData(S: TStream);
- var
- Index,ObjCount: TOlSize;
- Destroy: Boolean;
- Value: Longint;
- begin
- { Write list to Stream }
- S.WriteBuffer(STREAMKENNUNG,SizeOf(STREAMKENNUNG));
- Destroy := DestroyObjects;
- S.WriteBuffer(Destroy,SizeOf(Destroy));
- { write string length }
- Value := Length(FFIRFilter.FDescription);
- S.WriteBuffer(Value, SizeOf(Value));
- S.WriteBuffer(PChar(FFIRFilter.FDescription)^, Length(FFIRFilter.FDescription));
- ObjCount := Count;
- S.WriteBuffer(ObjCount,SizeOf(ObjCount));
- for Index := 0 to Count-1 do
- WriteObjectToStream(Items[Index],S);
- end;
- {== TMMFIRFilter ==============================================================}
- constructor TMMFIRFilter.Create(aOwner: TComponent);
- begin
- inherited Create(aOwner);
- FFilters := TMMFIRFilterList.Create;
- FFilters.OnChange := FiltersChanged;
- FFilters.FFIRFilter := Self;
- FOpen := False;
- FEnabled := defEnabled;
- FPFIR := nil;
- FPTempFIR := nil;
- FUpdating := False;
- FDescription:= 'Untitled';
- Ffs := defRate;
- FWindow := defWindow;
- fnCoeffs := defOrder;
- FChannel := defChannel;
- FUpdating := False;
- FResponse := nil;
- FSpectrum := nil;
- FTempBuffer := nil;
- CalcFilter;
- ErrorCode := ComponentRegistered(InitCode, Self, ClassName);
- if (ErrorCode <> 0) then RegisterFailed(InitCode, Self , ClassName);
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- destructor TMMFIRFilter.Destroy;
- begin
- Close;
- SetResponse(nil);
- SetSpectrum(nil);
- FFilters.Free;
- inherited
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Notification(aComponent: TComponent; Operation: TOperation);
- begin
- inherited Notification(aComponent, Operation);
- if (Operation = opRemove) then
- begin
- if (aComponent = FResponse) then
- FResponse := nil;
- if (aComponent = FSpectrum) then
- FSpectrum := nil;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.PcmOverflow;
- begin
- if assigned(FOnPcmOverflow) then FOnPcmOverflow(Self);
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SetCoeffs(pCoeffs: PFloatArray; nCoeffs: integer);
- begin
- if (pCoeffs <> nil) then
- begin
- if (nCoeffs > MAXTAPS) then
- raise EMMFIRError.Create('Only '+IntToStr(MAXTAPS)+' are allowed');
- // FFirFunc := ffUser;
- FnCoeffs := nCoeffs;
- GlobalMoveMem(pCoeffs^,FCoeffs,nCoeffs*sizeOf(Float));
- Change;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- function TMMFIRFilter.GetCoeffs: PFloatArray;
- begin
- Result := @FCoeffs;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- function TMMFIRFilter.LoadCoeffs(FName: TFileName): Boolean;
- var
- F: TextFile;
- S: string;
- nCoeffs,n: integer;
- Coeffs: array[0..MAXTAPS-1] of Float;
- begin
- Result := False;
- if FileExists(FName) then
- try
- {$I-}
- AssignFile(F, FName);
- Reset(F);
- try
- { read the number of coeffs }
- ReadLn(F, S);
- nCoeffs := StrToInt(S);
- n := 0;
- while not EOF(F) do
- begin
- ReadLn(F,S);
- Coeffs[n] := StrToFloat(S);
- inc(n);
- end;
- if (n = nCoeffs) then
- begin
- SetCoeffs(@Coeffs,nCoeffs);
- Result := True;
- end;
- finally
- CloseFile(F);
- end;
- except
- ;
- end;
- {$I+}
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- function TMMFIRFilter.SaveCoeffs(FName: TFileName): Boolean;
- var
- F: TextFile;
- n: integer;
- begin
- Result := False;
- try
- {$I-}
- AssignFile(F, FName);
- Rewrite(F);
- try
- { write the number of coeffs }
- WriteLn(F, IntToStr(FnCoeffs));
- for n := 0 to FnCoeffs-1 do
- begin
- WriteLn(F,FloatToStr(FCoeffs[n]));
- end;
- Result := True;
- finally
- CloseFile(F);
- end;
- except
- ;
- end;
- {$I+}
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SaveToIniFile(IniFile: TFileName; Section: string);
- var
- i: integer;
- begin
- if (IniFile <> '') then
- begin
- with TIniFile.Create(IniFile) do
- try
- Section := 'FIR.'+Section;
- WriteInteger(Section, 'Order', Order);
- WriteInteger(Section, 'Window', Ord(Window));
- WriteInteger(Section, 'Filters', Filters.Count);
- for i := 0 to Filters.Count-1 do
- with Filters[i] do
- begin
- WriteString(Section, 'Filter'+IntToStr(i)+' f1', FloatToStr(f1));
- WriteString(Section, 'Filter'+IntToStr(i)+' Gain', FloatToStr(Gain));
- end;
- finally
- Free;
- end;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.ReadFromIniFile(IniFile: TFileName; Section: string);
- var
- Cnt, i: integer;
- f1,Gain: Float;
- begin
- if (IniFile <> '') then
- begin
- with TIniFile.Create(IniFile) do
- try
- Section := 'FIR.'+Section;
- i := ReadInteger(Section, 'Order', -1);
- if (i > 0) then
- begin
- Filters.BeginUpdate;
- try
- FUpdating := True;
- Filters.FreeAll;
- Order := ReadInteger(Section, 'Order', defOrder);
- Window := TMMFFTWindow(ReadInteger(Section, 'Window', Ord(defWindow)));
- Description := Section;
- Cnt := ReadInteger(Section, 'Filters', 0);
- for i := 0 to Cnt-1 do
- begin
- f1 := StrToFloat(ReadString(Section, 'Filter'+IntToStr(i)+' f1', '0'));
- Gain := StrToFloat(ReadString(Section, 'Filter'+IntToStr(i)+' Gain', '0'));
- Filters.AddObject(TMMFIRFilterItem.CreateEx(f1,Gain));
- end;
- finally
- FUpdating := False;
- Filters.EndUpdate;
- end;
- end;
- finally
- Free;
- end;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SetFilters(aValue: TMMFIRFilterList);
- begin
- if (aValue <> FFilters) then FFilters.Assign(aValue);
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.FilterChanged(Sender: TObject);
- begin
- if not FUpdating then
- begin
- CalcFilter;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.FiltersChanged(Sender: TObject);
- begin
- if not FUpdating then
- begin
- CalcFilter;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SetDescription(aValue: String);
- begin
- if (aValue <> FDescription) then
- begin
- FDescription := aValue;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- Procedure TMMFIRFilter.SetChannel(aValue: TMMChannel);
- begin
- if (aValue <> FChannel) then
- begin
- FChannel := aValue;
- UpdateFilter;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SetSampleRate(aValue: Longint);
- begin
- if (aValue <> Ffs) then
- begin
- Ffs := MinMax(aValue,4000,100000);
- UpdateTempFilter(True);
- CalcFilter;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SetWindow(aValue: TMMFFTWindow);
- begin
- if (aValue <> FWindow) then
- begin
- FWindow := aValue;
- CalcFilter;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SetNCoeffs(aValue: integer);
- begin
- if (aValue <> FnCoeffs) then
- begin
- if (aValue mod 2 = 0) then inc(aValue);
- FnCoeffs := MinMax(aValue,0,MAXTAPS-1);
- CalcFilter;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SetResponse(aValue: TMMFIRResponse);
- begin
- if Longint(Self) = Longint(aValue) then exit;
- if (aValue <> FResponse) then
- begin
- if (aValue = nil) then NotifyResponse(opRemove);
- FResponse := aValue;
- NotifyResponse(opInsert);
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.NotifyResponse(Operation: TOperation);
- begin
- if (FResponse = nil) or
- (csLoading in ComponentState) or
- (csReading in ComponentState) then exit;
- if (Operation = opInsert) then
- begin
- FResponse.SetCoeffs(@FCoeffs,FnCoeffs);
- FResponse.SampleRate := Ffs;
- end
- else
- begin
- FResponse.SetCoeffs(nil,0);
- FResponse.SampleRate := 0;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.UpdateTempFilter(Init: Boolean);
- var
- wfx: TWaveFormatEx;
- begin
- DoneFIRFilter(FPTempFIR);
- if Init and (FSpectrum <> nil) then
- begin
- pcmBuildWaveHeader(@wfx, 16, 1, Ffs);
- FPTempFIR := InitFIRFilter(@wfx);
- FSpectrum.PCMWaveFormat := PPCMWaveFormat(@wfx)^;
- NotifySpectrum;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SetSpectrum(aValue: TMMSpectrum);
- begin
- if Longint(Self) = Longint(aValue) then exit;
- if (aValue <> FSpectrum) then
- begin
- if (aValue = nil) then
- begin
- FSpectrum.OnNeedData := nil;
- NotifySpectrum;
- UpdateTempFilter(False);
- end;
- FSpectrum := aValue;
- if (FSpectrum <> nil) then
- begin
- UpdateTempFilter(True);
- FSpectrum.Window := fwRectangular;
- FSpectrum.OnNeedData := SpectrumNeedData;
- NotifySpectrum;
- end;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.NotifySpectrum;
- begin
- if (FSpectrum = nil) or
- (csLoading in ComponentState) or
- (csReading in ComponentState) then exit;
- FSpectrum.ResetData;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SpectrumNeedData(Sender: TObject);
- var
- wfx: TWaveFormatEx;
- BufI,BufO: array[0..8192] of Smallint;
- begin
- if (Sender <> nil) then
- with TMMSpectrum(Sender) do
- begin
- pcmBuildWaveHeader(@wfx, 16, 1, Ffs);
- PCMWaveFormat := PPCMWaveFormat(@wfx)^;
- ResetFIRFilter(FPTempFIR);
- SetFIRFilter(FPTempFir, @FCoeffs, FnCoeffs, 0);
-
- GlobalFillMem(BufI,sizeOf(BufI),0);
- BufI[0] := 21500;
-
- DoFIRFilterShort(FPTempFir, @BufI, @BufO, BytesPerSpectrum);
- RefreshPCMData(@BufO);
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.CalcFilter;
- var
- i,Cnt: integer;
- Pts: array[0..MAXFREQS-1] of TMMFilterPoint;
- begin
- if not (csLoading in ComponentState) and
- not (csReading in ComponentState) then
- begin
- Filters.Sort;
-
- Cnt := Filters.Count;
- for i := 0 to Cnt-1 do
- with Filters[i] do
- begin
- Pts[i].Freq := Ff1;
- Pts[i].Amp := FGain;
- end;
- { make sure we have at least two valid points }
- if (Cnt = 0) then
- begin
- Pts[0].Freq := 0;
- Pts[0].Amp := 0;
- inc(Cnt);
- end;
- if (Cnt = 1) then
- begin
- Pts[1].Freq := Ffs/2;
- Pts[1].Amp := 0;
- inc(Cnt);
- end;
- FIR_CoeffsEx(@Pts, Cnt, Ffs, FnCoeffs, @FCoeffs);
- FIR_Window(FWindow, FnCoeffs, @FCoeffs);
- Change;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.UpdateFilter;
- begin
- if FOpen and (FPFIR <> nil) then
- begin
- FCleanup := wioSamplesToBytes(PWaveFormat,FnCoeffs);
- SetFIRFilter(FPFIR, @FCoeffs, FnCoeffs, ord(FChannel));
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Change;
- begin
- UpdateFilter;
- NotifyResponse(opInsert);
- NotifySpectrum;
- if assigned(FOnChange) then FOnChange(Self);
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Loaded;
- begin
- inherited Loaded;
- CalcFilter;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.SetPWaveFormat(aValue: PWaveFormatEx);
- begin
- if (aValue <> nil) then
- begin
- if not (csDesigning in ComponentState) then
- if not pcmIsValidFormat(aValue) or (aValue^.wBitsPerSample = 8) then
- raise EMMFIRError.Create(LoadResStr(IDS_INVALIDFORMAT));
- SampleRate := aValue^.nSamplesPerSec;
- end;
- inherited SetPWaveFormat(aValue);
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Assign(Source: TPersistent);
- begin
- if (Source is TMMFIRFilter) then
- begin
- if (Source <> nil) then
- begin
- Channel := TMMFIRFilter(Source).Channel;
- Enabled := TMMFIRFilter(Source).Enabled;
- Description:= TMMFIRFilter(Source).Description;
- Order := TMMFIRFilter(Source).Order;
- Window := TMMFIRFilter(Source).Window;
- SampleRate := TMMFIRFilter(Source).SampleRate;
- Filters := TMMFIRFilter(Source).Filters;
- end;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Open;
- begin
- if not FOpen then
- begin
- if pcmIsValidFormat(PWaveFormat) then
- begin
- FPFIR := InitFIRFilter(PWaveFormat);
- if (FPFIR = nil) then OutOfMemoryError
- else
- begin
- FTempBuffer := GlobalAllocMem(Max(QUEUE_READ_SIZE,BufferSize));
- FOpen := True;
- end;
- end;
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Start;
- begin
- UpdateFilter;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Close;
- begin
- if FOpen then
- begin
- FOpen := False;
- DoneFIRFilter(FPFIR);
- GlobalFreeMem(Pointer(FTempBuffer));
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Process(Buffer: PChar; nBytes: Longint);
- begin
- { process the buffer trough the delay line }
- if (FPFIR <> nil) then
- begin
- if DoFIRFilterShort(FPFIR, Buffer, FTempBuffer, nBytes) then
- GlobalSynchronize(PcmOverflow);
- GlobalMoveMem(FTempBuffer^,Buffer^,nBytes);
- end;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- function TMMFIRFilter.CleanUp(Buffer: PChar; Length: integer): Longint;
- begin
- { process the remaining bytes in the delay lines }
- if (FPFIR <> nil) and (FCleanup > 0) then
- begin
- FCleanup := Max(FCleanup - Length,0);
- FillChar(Buffer^, Length, 0);
- Process(Buffer, Length);
- end;
- { return the remaining bytes to process }
- Result := FCleanup;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Opened;
- begin
- inherited Opened;
- Open;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Closed;
- begin
- Close;
- inherited Closed;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.Started;
- begin
- inherited Started;
- Start;
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.BufferReady(lpwh: PWaveHdr);
- begin
- if Enabled then
- begin
- Process(lpwh^.lpData,lpwh^.dwBytesRecorded);
- end;
- inherited BufferReady(lpwh);
- end;
- {-- TMMFIRFilter --------------------------------------------------------------}
- procedure TMMFIRFilter.BufferLoad(lpwh: PWaveHdr; var MoreBuffers: Boolean);
- var
- aLength: Longint;
- begin
- inherited BufferLoad(lpwh, MoreBuffers);
- if Enabled and FOpen then
- begin
- if not MoreBuffers then
- begin
- aLength := lpwh^.dwBufferLength;
- if Cleanup(lpwh^.lpData, aLength) > 0 then MoreBuffers := True;
- lpwh^.dwBytesRecorded := aLength;
- end
- else Process(lpwh^.lpData, lpwh^.dwBytesRecorded);
- end;
- end;
- {==============================================================================}
- {-- TMMFIRResponse --}
- {==============================================================================}
- constructor TMMFIRResponse.Create(aOwner: TComponent);
- begin
- inherited Create(aOwner);
- Font.Style := [fsBold];
- Ffs := defRate;
- FnCoeffs := 0;
- FGridColor := defGridColor;
- FAxisColor := defAxisColor;
- FRespColor := defRespColor;
- FCoeffColor := defCoeffColor;
- FDBScale := defDBScale;
- FNormalized := defNormalized;
- FShowCoeffs := defShowCoeffs;
- FScaleColor := defScaleColor;
- Color := defColor;
- Width := defWidth;
- Height := defHeight;
- ErrorCode := ComponentRegistered(InitCode, Self, ClassName);
- if (ErrorCode <> 0) then RegisterFailed(InitCode, Self , ClassName);
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- destructor TMMFIRResponse.Destroy;
- begin
- inherited Destroy;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.AdjustClientSize;
- begin
- FClient := BeveledRect;
- InflateRect(FClient,-30,-30);
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.SetBounds(aLeft, aTop, aWidth, aHeight: integer);
- begin
- inherited SetBounds(aLeft, aTop, aWidth, aHeight);
- AdjustClientSize;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.Changed;
- begin
- AdjustClientSize;
- inherited Changed;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.SetCoeffs(pCoeffs: PFloatArray; nCoeffs: integer);
- begin
- FnCoeffs := nCoeffs;
- if (FnCoeffs > 0) then
- begin
- GlobalMoveMem(pCoeffs^,FCoeffs,FnCoeffs*sizeOf(Float));
- end;
- Invalidate;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.SetBoolean(index: integer; aValue: Boolean);
- begin
- case index of
- 0: if (FShowCoeffs = aValue) then exit else FShowCoeffs := aValue;
- 1: if (FDBScale = aValue) then exit else FDBScale := aValue;
- 2: if (FNormalized = aValue) then exit else FNormalized := aValue;
- end;
- Invalidate;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.SetSampleRate(aValue: Longint);
- begin
- if (aValue <> Ffs) then
- begin
- Ffs := MinMax(aValue,4000,100000);
- Invalidate;
- end;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.SetColors(index: integer; aValue: TColor);
- begin
- case index of
- 0: if (aValue = FGridColor) then exit else FGridColor := aValue;
- 1: if (aValue = FAxisColor) then exit else FAxisColor := aValue;
- 2: if (aValue = FRespColor) then exit else FRespColor := aValue;
- 3: if (aValue = FCoeffColor) then exit else FCoeffColor := aValue;
- 4: if (aValue = FScaleColor) then exit else FScaleColor := aValue;
- end;
- Invalidate;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.VLineDoted(aCanvas:TCanvas;x,y1,y2:integer;Clr:TColorRef);
- var
- DC: HDC;
- begin
- DC := aCanvas.Handle;
- if (y1 > y2) then SwapInt(y1,y2);
- while y1 < y2 do
- begin
- SetPixel(DC,x,y1,Clr);
- inc(y1,2);
- end;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.HLineDoted(aCanvas:TCanvas;x1,x2,y:integer;Clr:TColorRef);
- var
- DC: HDC;
- begin
- DC := aCanvas.Handle;
- if (x1 > x2) then SwapInt(x1,x2);
- while x1 < x2 do
- begin
- SetPixel(DC,x1,y,Clr);
- inc(x1,2);
- end;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.DrawBackground(Canvas: TCanvas; Client: TRect);
- var
- i,x,y: integer;
- str: string;
- th,tw: integer;
- W,H: integer;
- Clr: Longint;
- NGrids: integer;
- begin
- with Canvas,Client do
- begin
- W:= Right-Left;
- H:= Bottom-Top;
- Pen.Color := FAxisColor;
- Font.Color := FScaleColor;
- MoveTo(Left,Top);
- LineTo(Left,Bottom);
- LineTo(Right,Bottom);
- Font := Self.Font;
- SetTextAlign(Handle,TA_BASELINE or TA_CENTER);
- (*
- case FFirFunc of
- ffLowPass : str := Format('Low Pass:';
- ffHighPass: str := Format('High Pass:';
- ffBandPass: str := Format('Band Pass:';
- ffBandStop: str := Format('Band Stop: f1 %d, f2 %d, %d taps',[;
- ffUser : str := Format('User: f1 %d, f2 %d';
- str := Format(str+' Sample rate %d, f1 %d, %d taps',
- [Ffs,0,FnCoeffs]);
- *)
- str := Format('FIR Filter Response: %f Khz, %d taps',[Ffs/1000,FnCoeffs]);
- TextOut(Left+W div 2,Top-10,str);
- Font.Handle:= CreateFont(-9,0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,
- OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,
- VARIABLE_PITCH or FF_SWISS,'arial');
- SetTextAlign(Handle,TA_RIGHT);
- Clr := ColorToRGB(GridColor);
- th := TextHeight('W');
- if FNormalized then
- tw := TextWidth('0.5')
- else
- tw := TextWidth(' '+IntToStr(Ffs div 2));
- { calc the number of steps required }
- NGrids := 20;
- while (H div NGrids < th) or
- (W div NGrids < tw) do
- begin
- NGrids := NGrids div 2;
- if NGrids = 1 then break;
- end;
- for i := 0 to NGrids do
- begin
- y := (H*i)div NGrids;
- MoveTo(Left, Top+y);
- LineTo(Left-5, Top+y);
- if (i <> NGrids) then
- HLineDoted(Canvas,Left+1,Left+W,Top+y,Clr);
- if FDBScale then
- begin
- if (i = 0) then
- str := '0'
- else
- str := Format('-%d',[(i*100)div NGrids]);
- end
- else str := Format('%d%',[100-(i*100)div NGrids]);
- TextOut(Left-7,Top+y-(th div 2),str);
- end;
- if FDBScale then
- TextOut(Left-8,Bottom+10,'dB')
- else
- TextOut(Left-7,Bottom+10,'%');
- // mark the frequency scale (linear as a function of sample frequency)
- SetTextAlign(Handle,TA_CENTER or TA_TOP);
- for i := 0 to NGrids do
- begin
- x := (i*W) div NGrids;
- MoveTo(Left+x,Bottom);
- LineTo(Left+x,Bottom+5);
- if (i > 0) then
- VLineDoted(Canvas,Left+x,Top,Top+H,Clr);
- if FNormalized then
- str := Format('%f',[(0.5*i)/NGrids])
- else
- str := Format('%d',[(Ffs div 2)*i div NGrids]);
- TextOut(Left+x,Bottom+10,str);
- end;
- if not FNormalized then
- TextOut(Left+x+(tw div 2)+5,Bottom+10,' Hz');
- end;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.DrawImpulseResponse(Canvas: TCanvas; Client: TRect);
- const
- HS = 4;
- var
- W,H,i,ftype: integer;
- coeffs: array[0..4096] of Float;
- begin
- if (FnCoeffs > 0) then
- with Canvas,Client do
- begin
- if (FnCoeffs and 1 <> 0) then
- ftype := 1
- else
- ftype := 2;
- H := Bottom-Top;
- W := Right-Left;
- FIR_Response(ftype,FnCoeffs,@FCoeffs,FDBScale,W,@coeffs);
- FIR_NormaliseResponse(FDBScale,W,@coeffs);
- Pen.Color := FRespColor;
- MoveTo(Left,Bottom-Trunc(coeffs[0]*H));
- for i := 0 to W-1 do
- begin
- LineTo(Left+i,Bottom-Trunc(coeffs[i]*H));
- end;
- if FShowCoeffs then
- begin
- // last thing: draw the filter itself
- Pen.Color := FCoeffColor;
- MoveTo(Left,Bottom-Trunc(FCoeffs[0]));
- if (FnCoeffs > 1) then
- for i := 0 to FnCoeffs-1 do
- begin
- LineTo(Left+(W*i)div (FnCoeffs-1),
- Bottom-Trunc(FCoeffs[i]*H));
- end;
- end;
- end;
- end;
- {-- TMMFIRResponse ------------------------------------------------------------}
- procedure TMMFIRResponse.Paint;
- begin
- inherited Paint;
- DrawBackground(Canvas,FClient);
- DrawImpulseResponse(Canvas,FClient);
- end;
- Initialization
- { register filter class for streaming ! }
- DoRegisterClass(@TMMFIRFilterItem.Load,
- @TMMFIRFilterItem.Store,
- TMMFIRFilterItem);
- end.