MMFIR.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:53k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {========================================================================}
  2. {=                (c) 1995-98 SwiftSoft Ronald Dittrich                 =}
  3. {========================================================================}
  4. {=                          All Rights Reserved                         =}
  5. {========================================================================}
  6. {=  D 01099 Dresden             = Fax.: +49 (0)351-8037944              =}
  7. {=  Loewenstr.7a                = info@swiftsoft.de                     =}
  8. {========================================================================}
  9. {=  Actual versions on http://www.swiftsoft.de/mmtools.html             =}
  10. {========================================================================}
  11. {=  This code is for reference purposes only and may not be copied or   =}
  12. {=  distributed in any format electronic or otherwise except one copy   =}
  13. {=  for backup purposes.                                                =}
  14. {=                                                                      =}
  15. {=  No Delphi Component Kit or Component individually or in a collection=}
  16. {=  subclassed or otherwise from the code in this unit, or associated   =}
  17. {=  .pas, .dfm, .dcu, .asm or .obj files may be sold or distributed     =}
  18. {=  without express permission from SwiftSoft.                          =}
  19. {=                                                                      =}
  20. {=  For more licence informations please refer to the associated        =}
  21. {=  HelpFile.                                                           =}
  22. {========================================================================}
  23. {=  $Date: 09.09.98 - 12:05:10 $                                        =}
  24. {========================================================================}
  25. unit MMFIR;
  26. {$I COMPILER.INC}
  27. interface
  28. uses
  29. {$IFDEF WIN32}
  30.     Windows,
  31. {$ELSE}
  32.     WinTypes,
  33.     WinProcs,
  34. {$ENDIF}
  35.     SysUtils,
  36.     Messages,
  37.     Classes,
  38.     Graphics,
  39.     Controls,
  40.     Forms,
  41.     Dialogs,
  42.     ExtCtrls,
  43.     Menus,
  44.     MMSystem,
  45.     MMObj,
  46.     MMDSPObj,
  47.     MMObjLst,
  48.     MMRegs,
  49.     MMPCMSup,
  50.     MMWaveIO,
  51.     MMUtils,
  52.     MMMuldiv,
  53.     MMMath,
  54.     MMFFT,
  55.     MMSpectr,
  56.     MMFIRFlt;
  57. const
  58.     {$IFDEF CBUILDER3} {$EXTERNALSYM MAXFREQS} {$ENDIF}
  59.     MAXFREQS = 256;
  60.     {$IFDEF CBUILDER3} {$EXTERNALSYM MAXTAPS} {$ENDIF}
  61.     MAXTAPS  = 400;
  62. const
  63.     {$IFDEF CBUILDER3} {$EXTERNALSYM defEnabled} {$ENDIF}
  64.     defEnabled      = True;
  65.     {$IFDEF CBUILDER3} {$EXTERNALSYM defColor} {$ENDIF}
  66.     defColor        = clWhite;
  67.     {$IFDEF CBUILDER3} {$EXTERNALSYM defGridColor} {$ENDIF}
  68.     defGridColor    = clGray;
  69.     {$IFDEF CBUILDER3} {$EXTERNALSYM defAxisColor} {$ENDIF}
  70.     defAxisColor    = clBlack;
  71.     {$IFDEF CBUILDER3} {$EXTERNALSYM defRespColor} {$ENDIF}
  72.     defRespColor    = clRed;
  73.     {$IFDEF CBUILDER3} {$EXTERNALSYM defCoeffColor} {$ENDIF}
  74.     defCoeffColor   = clBlue;
  75.     {$IFDEF CBUILDER3} {$EXTERNALSYM defScaleColor} {$ENDIF}
  76.     defScaleColor   = clBlack;
  77.     {$IFDEF CBUILDER3} {$EXTERNALSYM defDBScale} {$ENDIF}
  78.     defDBScale      = True;
  79.     {$IFDEF CBUILDER3} {$EXTERNALSYM defNormalized} {$ENDIF}
  80.     defNormalized   = False;
  81.     {$IFDEF CBUILDER3} {$EXTERNALSYM defShowCoeffs} {$ENDIF}
  82.     defShowCoeffs   = False;
  83.     {$IFDEF CBUILDER3} {$EXTERNALSYM defChannel} {$ENDIF}
  84.     defChannel      = chBoth;
  85.     {$IFDEF CBUILDER3} {$EXTERNALSYM defOrder} {$ENDIF}
  86.     defOrder        = 21;
  87.     {$IFDEF CBUILDER3} {$EXTERNALSYM defRate} {$ENDIF}
  88.     defRate         = 4000;
  89.     {$IFDEF CBUILDER3} {$EXTERNALSYM defWindow} {$ENDIF}
  90.     defWindow       = fwHamming;
  91.     {$IFDEF CBUILDER3} {$EXTERNALSYM defWidth} {$ENDIF}
  92.     defWidth        = 300;
  93.     {$IFDEF CBUILDER3} {$EXTERNALSYM defHeight} {$ENDIF}
  94.     defHeight       = 300;
  95. type
  96.     TMMFIRFilter    = class;
  97.     TMMFIRResponse  = class;
  98.     EMMFIRError     = class(Exception);
  99.     {-- TMMFIRFilterItem ------------------------------------------------------}
  100.     TMMFIRFilterItem = class(TObject)
  101.     private
  102.        Ff1      : Float;
  103.        FGain    : Float;
  104.        FOnChange: TNotifyEvent;
  105.        procedure SetValue(index: integer; aValue: Float);
  106.        procedure Store(S: TStream); virtual;
  107.        procedure Load(S: TStream); virtual;
  108.     protected
  109.        procedure Changed; virtual;
  110.     public
  111.        constructor Create;
  112.        constructor CreateEx(af1,aGain: Float);
  113.        procedure Assign(Source: TObject);
  114.        procedure SetParams(af1, aGain: Float);
  115.        property OnChange: TNotifyEvent read FOnChange write FOnChange;
  116.        property f1: Float index 0 read Ff1 write SetValue;
  117.        property Gain: Float index 1 read FGain write SetValue;
  118.     end;
  119.     {-- TMMFIRFilterList ------------------------------------------------------}
  120.     TMMFIRFilterList = class(TObjectList)
  121.     private
  122.        FFIRFilter: TMMFIRFilter;
  123.        procedure SetFilter(Index: integer; Filter: TMMFIRFilterItem);
  124.        function  GetFilter(Index: integer): TMMFIRFilterItem;
  125.     protected
  126.        procedure DefineProperties(Filer: TFiler); override;
  127.        procedure ReadData(S: TStream); override;
  128.        procedure WriteData(S: TStream); override;
  129.     public
  130.        function  AddObject(Item: TObject): TOLSize; override;
  131.        procedure Sort;
  132.        procedure Assign(Source: TPersistent); override;
  133.        property  Items[Index: integer]: TMMFIRFilterItem read GetFilter write SetFilter; default;
  134.     end;
  135.     {-- TMMFIRFilter ----------------------------------------------------------}
  136.     TMMFIRFilter    = class(TMMDSPComponent)
  137.     private
  138.        FOpen          : Boolean;
  139.        FEnabled       : Boolean;
  140.        FUpdating      : Boolean;
  141.        FDescription   : String;
  142.        Fncoeffs       : integer;
  143.        Fcoeffs        : array[0..MAXTAPS-1] of Float;
  144.        Ffs            : Longint;
  145.        FFilters       : TMMFIRFilterList;
  146.        FWindow        : TMMFFTWindow;
  147.        FCleanup       : Longint;
  148.        FPFIR          : PFIRFilter;
  149.        FPTempFIR      : PFIRFilter;
  150.        FChannel       : TMMChannel;
  151.        FTempBuffer    : PChar;
  152.        FResponse      : TMMFIRResponse;
  153.        FSpectrum      : TMMSpectrum;
  154.        FOnChange      : TNotifyEvent;
  155.        FOnPcmOverflow : TNotifyEvent;
  156.        procedure SetWindow(aValue: TMMFFTWindow);
  157.        procedure SetChannel(aValue: TMMChannel);
  158.        procedure SetSampleRate(aValue: Longint);
  159.        procedure SetNCoeffs(aValue: integer);
  160.        procedure SetDescription(aValue: String);
  161.        procedure SetFilters(aValue: TMMFIRFilterList);
  162.        procedure SetResponse(aValue: TMMFIRResponse);
  163.        procedure SetSpectrum(aValue: TMMSpectrum);
  164.        procedure NotifyResponse(Operation: TOperation);
  165.        procedure NotifySpectrum;
  166.        procedure SpectrumNeedData(Sender: TObject);
  167.        procedure FiltersChanged(Sender: TObject);
  168.        procedure FilterChanged(Sender: TObject);
  169.        procedure UpdateTempFilter(Init: Boolean);
  170.        procedure CalcFilter;
  171.        procedure UpdateFilter;
  172.     protected
  173.        procedure Change; virtual;
  174.        procedure Loaded; override;
  175.        procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  176.        procedure SetPWaveFormat(aValue: PWaveFormatEx); override;
  177.        procedure Assign(Source: TPersistent); override;
  178.        procedure Opened; override;
  179.        procedure Started; override;
  180.        procedure Closed; override;
  181.        procedure PcmOverflow; dynamic;
  182.        procedure BufferReady(lpwh: PWaveHdr); override;
  183.        procedure BufferLoad(lpwh: PWaveHdr; var MoreBuffers: Boolean); override;
  184.     public
  185.        constructor Create(aOwner: TComponent); override;
  186.        destructor  Destroy; override;
  187.        procedure  Open;
  188.        procedure  Start;
  189.        procedure  Close;
  190.        procedure  Process(Buffer: PChar; nBytes: Longint);
  191.        function   CleanUp(Buffer: PChar; Length: integer): Longint;
  192.        procedure  SetCoeffs(pCoeffs: PFloatArray; nCoeffs: integer);
  193.        function   GetCoeffs: PFloatArray;
  194.        function   LoadCoeffs(FName: TFileName): Boolean;
  195.        function   SaveCoeffs(FName: TFileName): Boolean;
  196.        procedure  SaveToIniFile(IniFile: TFileName; Section: string);
  197.        procedure  ReadFromIniFile(IniFile: TFileName; Section: string);
  198.     published
  199.        property OnChange: TNotifyEvent read FOnChange write FOnChange;
  200.        property OnPcmOverflow: TNotifyEvent read FOnPcmOverflow write FOnPcmOverflow;
  201.        property Input;
  202.        property Output;
  203.        property Enabled: Boolean read FEnabled write FEnabled default defEnabled;
  204.        property SampleRate: Longint read Ffs write SetSampleRate default defRate;
  205.        property Order: integer read FnCoeffs write SetNCoeffs default defOrder;
  206.        property Description: String read FDescription write SetDescription stored False;
  207.        property Filters: TMMFIRFilterList read FFilters write SetFilters;
  208.        property Response: TMMFIRResponse read FResponse write SetResponse;
  209.        property Spectrum: TMMSpectrum read FSpectrum write SetSpectrum;
  210.        property Channel: TMMChannel read FChannel write SetChannel default defChannel;
  211.        property Window: TMMFFTWindow read FWindow write SetWindow default defWindow;
  212.     end;
  213.     {-- TMMFIRResponse --------------------------------------------------------}
  214.     TMMFIRResponse = class(TMMGraphicControl)
  215.     private
  216.        FClient    : TRect;
  217.        FDBScale   : Boolean;
  218.        FNormalized: Boolean;
  219.        FShowCoeffs: Boolean;
  220.        FGridColor : TColor;
  221.        FAxisColor : TColor;
  222.        FRespColor : TColor;
  223.        FCoeffColor: TColor;
  224.        FScaleColor: TColor;
  225.        FnCoeffs   : integer;
  226.        FCoeffs    : array[0..MAXTAPS-1] of Float;
  227.        Ffs        : Longint;
  228.        procedure SetColors(index: integer; aValue: TColor);
  229.        procedure SetBoolean(index: integer; aValue: Boolean);
  230.        procedure SetSampleRate(aValue: Longint);
  231.        procedure AdjustClientSize;
  232.        procedure VLineDoted(aCanvas: TCanvas; x, y1, y2: integer; Clr: TColorRef);
  233.        procedure HLineDoted(aCanvas: TCanvas; x1, x2, y: integer; Clr: TColorRef);
  234.        procedure DrawBackground(Canvas: TCanvas; Client: TRect);
  235.        procedure DrawImpulseResponse(Canvas: TCanvas; Client: TRect);
  236.     protected
  237.        procedure   Paint; override;
  238.        procedure   Changed; override;
  239.        procedure   SetBounds(aLeft, aTop, aWidth, aHeight: integer); override;
  240.     public
  241.        constructor Create(aOwner: TComponent); override;
  242.        destructor  Destroy; override;
  243.        procedure SetCoeffs(pCoeffs: PFloatArray; nCoeffs: integer);
  244.     published
  245.        property Color default defColor;
  246.        property ParentColor;
  247.        property Align;
  248.        property Bevel;
  249.        property Font;
  250.        property ParentFont;
  251.        property Visible;
  252.        property Width default defWidth;
  253.        property Height default defHeight;
  254.        property GridColor: TColor index 0 read FGridColor write SetColors default defGridColor;
  255.        property AxisColor: TColor index 1 read FAxisColor write SetColors default defAxisColor;
  256.        property ResponseColor: TColor index 2 read FRespColor write SetColors default defRespColor;
  257.        property CoeffColor: TColor index 3 read FCoeffColor write SetColors default defCoeffColor;
  258.        property ScaleColor: TColor index 4 read FScaleColor write SetColors default defScaleColor;
  259.        property ShowCoeffs: Boolean index 0 read FShowCoeffs write SetBoolean default defShowCoeffs;
  260.        property DBScale: Boolean index 1 read FDBScale write SetBoolean default defDBScale;
  261.        property Normalized: Boolean index 2 read FNormalized write SetBoolean default defNormalized;
  262.        property SampleRate: Longint read Ffs write SetSampleRate default defRate;
  263.     end;
  264. {-- Coeff generation and response --}
  265. type
  266.     TMMFIRFunction  = (ffLowPass,ffHighPass,ffBandPass,ffBandStop);
  267.     PMMFilterPoint  = ^TMMFilterPoint;
  268.     TMMFilterPoint  = record
  269.        Freq: Float;
  270.        Amp : Float;
  271.     end;
  272.     PMMFilterPoints = ^TMMFilterPoints;
  273.     TMMFilterPoints = array[0..0] of TMMFilterPoint;
  274. procedure FIR_Coeffs(FIRType: TMMFIRFunction; SRate: integer;
  275.                      f1,f2: Float; N: integer; hh: PFloatArray);
  276. procedure FIR_CoeffsEx(Points: PMMFilterPoints; nPoints, SRate, N: integer;
  277.                        hh: PFloatArray);
  278. procedure FIR_Window(Window: TMMFFTWindow; N: integer; hh: PFloatArray);
  279. procedure FIR_Response (FirType,N: integer; hh: PFloatArray; dBScale: Boolean;
  280.        numPoints: integer; points: PFloatArray);
  281. procedure FIR_NormaliseResponse(dbscale: Boolean; NumPoints: integer; points: PFloatArray);
  282. {$O-}
  283. implementation
  284. uses IniFiles;
  285. const
  286.    STREAMKENNUNG : Longint = $00524946; { 'FIR ' }
  287. {==============================================================================}
  288. { note: delay occurs by ~ncoeff/2 samples                                      }
  289. {==============================================================================}
  290. procedure FIR_Filter(pcoeffs: PFloatArray; ncoeffs: integer;
  291.                      DLine: PLongArray; Data: PSmallArray; samples: integer);
  292. var
  293.    i,j,ki: integer;
  294.    y: Float;
  295.    idx: integer;
  296.    mask: integer;
  297. begin
  298.    mask := MAXTAPS-1;
  299.    idx := MAXTAPS-nCoeffs;
  300.    for i := 0 to samples-1 do
  301.    begin
  302.       ki := idx;
  303.       DLine[idx] := Data[i];
  304.       idx := (idx+1) and mask;
  305.       y := 0.0;
  306.       for j := 0 to ncoeffs-1 do
  307.       begin
  308.          ki := (ki-1) and mask;
  309.         y := y + DLine[ki] * pcoeffs[j];
  310.       end;
  311.       Data[i] := MinMax(Trunc(y),-32767,32767);
  312.    end;
  313. end;
  314. {==============================================================================}
  315. { interpolate to a y_value for a x_val using a table of points for x and y     }
  316. { returns the y_val or FALSE if error, extrapolates if x_val is outside data   }
  317. function FIR_Interpolate(ind_x, dep_y: PFloatArray; nPnts: integer;
  318.                          x_val: Float; var y_val: Float): Boolean;
  319. var
  320.    i: integer;
  321.    xrange: Float;
  322. begin
  323.    Result := False;
  324.    if (nPnts <= 0) then exit;
  325.    if (nPnts = 1) then
  326.    begin
  327.       y_val := dep_y[0];
  328.       Result := True;
  329.       exit;
  330.    end;
  331.    i := 0;
  332.    while (i < nPnts) and (x_val > ind_x[i]) do inc(i); // get to pair
  333.    if (i = nPnts) then                                 // extrapolate at end
  334.    begin
  335.       dec(i);
  336.       xrange := ind_x[i] - ind_x[i-1];
  337.       if (xrange = 0) then exit;
  338.       y_val := dep_y[i]+(x_val-ind_x[i])*(dep_y[i]-dep_y[i-1])/xrange;
  339.       Result := True;
  340.       exit;
  341.    end;
  342.    if (x_val = ind_x[i]) then
  343.    begin
  344.       y_val := dep_y[i];
  345.       Result := True;
  346.       exit;
  347.    end;
  348.    if (i = 0) then                                  // extrapolate at begining
  349.    begin
  350.       xrange := ind_x[i+1] - ind_x[i];
  351.       if (xrange = 0) then exit;
  352.       y_val := dep_y[i] - (ind_x[i]-x_val)*(dep_y[i+1]-dep_y[i]) /xrange;
  353.       Result := True;
  354.       exit;
  355.    end;
  356.    xrange := ind_x[i] - ind_x[i-1];
  357.    if (xrange = 0) then exit;
  358.    y_val :=  dep_y[i-1]+(x_val-ind_x[i-1])*(dep_y[i]-dep_y[i-1])/xrange;
  359.    Result := True;
  360. end;
  361. {==============================================================================}
  362. procedure FIR_CoeffsEx(Points: PMMFilterPoints; nPoints, SRate,
  363.                        N: integer; hh: PFloatArray);
  364. var
  365.    xt, q: Float;
  366.    m, i, j: integer;
  367.    freq,amp,dnpi: array [0..MAXFREQS-1] of Float;
  368.    a: array [0..MAXTAPS-1] of Float;
  369. begin
  370.    if (nPoints > MAXFREQS) then nPoints := MAXFREQS;
  371.    for i := 0 to nPoints-1 do
  372.    begin
  373.       freq[i] := Points[i].Freq/SRate;
  374.       amp[i]  := pow(10.0,Points[i].Amp/20.0);
  375.    end;
  376.    m := (N + 1) div 2;
  377.    q := 2 * M_PI / N;
  378.    for i := 0 to nPoints-1 do           // find positions of freqs in window
  379.    begin
  380.       dnpi[i] := N * freq[i] + 1.0;
  381.       if (dnpi[i] < 0) then dnpi[i] := 0;
  382.       if (dnpi[i] > N) then dnpi[i] := N;
  383.    end;
  384.    // set a[] array to the amp[] vals at each npi[] position
  385.    for j := 0 to m do
  386.    begin
  387.       FIR_Interpolate(@dnpi,@amp,nPoints,j,a[j]);
  388.    end;
  389.    for i := 1 to m do  // Calculate the coefficient array
  390.    begin
  391.       xt := a[1] / 2.0;
  392.       for j := 2 to m do
  393.           xt := xt + a[j] * cos(q*((m-i)*(j-1)));
  394.       hh[i-1] := 2.0 * xt / N;
  395.    end;
  396.    for i := m+1 to n do   // copy first n/2 coeff into last n/2 coeff
  397.        hh[i-1] := hh[2*m-i-1];
  398. end;
  399. {==============================================================================}
  400. procedure FIR_Coeffs(FIRType: TMMFIRFunction; SRate: integer; f1,f2: Float;
  401.                      N: integer; hh: PFloatArray);
  402. var
  403.    k: integer;
  404.    mm,a: Float;
  405.    fg,fg2: Float;
  406. begin
  407.    case FIRType of
  408.         ffLowpass:
  409.         begin
  410.            fg := f1/SRate;
  411.            { build lowpass }
  412.            for k := 0 to N-1 do
  413.            begin
  414.               mm := k-(N-1)/2.0;
  415.               if (mm = 0) then
  416.                   hh[k] := (fg*2*M_PI)/M_PI
  417.               else
  418.                   hh[k] := sin(mm*fg*2*M_PI)/(mm*M_PI);
  419.            end;
  420.         end;
  421.         ffHighpass:
  422.         begin
  423.            fg := f1/SRate;
  424.            { subtract a low pass from allpass }
  425.            for k := 0 to N-1 do
  426.            begin
  427.               { simulate a allpass }
  428.               if k = N div 2 then
  429.                  a := 1.0
  430.               else
  431.                  a := 0.0;
  432.               { the lowpass }
  433.               mm := k-(N-1)/2.0;
  434.               if (mm = 0) then
  435.                   hh[k] := a-((fg*2*M_PI)/M_PI)
  436.               else
  437.                   hh[k] := a-(sin(mm*fg*2*M_PI)/(mm*M_PI));
  438.            end;
  439.         end;
  440.         ffBandpass:
  441.         begin
  442.            fg := f1/SRate;
  443.            fg2 := f2/SRate;
  444.            { subtract a low pass from a low pass }
  445.            for k := 0 to N-1 do
  446.            begin
  447.               mm := k-(N-1)/2.0;
  448.               if (mm = 0) then
  449.                   hh[k] := ((fg2*2*M_PI)/M_PI)-((fg*2*M_PI)/M_PI)
  450.               else
  451.                   hh[k] := (sin(mm*fg2*2*M_PI)/(mm*M_PI))-(sin(mm*fg*2*M_PI)/(mm*M_PI));
  452.            end;
  453.         end;
  454.         ffBandstop:
  455.         begin
  456.            fg := f1/SRate;
  457.            fg2:= f2/SRate;
  458.            { build a bandpass and subtract it from a allpass }
  459.            for k := 0 to N-1 do
  460.            begin
  461.               { simulate a allpass }
  462.               if k = N div 2 then
  463.                  a := 1.0
  464.               else
  465.                  a := 0.0;
  466.               mm := k-(N-1)/2.0;
  467.               if (mm = 0) then
  468.                   hh[k] := a-((fg2*2*M_PI)/M_PI)-((fg*2*M_PI)/M_PI)
  469.               else
  470.                   hh[k] := a-(sin(mm*fg2*2*M_PI)/(mm*M_PI))-(sin(mm*fg*2*M_PI)/(mm*M_PI));
  471.            end;
  472.         end;
  473.    end;
  474. end;
  475. {==============================================================================}
  476. procedure FIR_Window(Window: TMMFFTWindow; N: integer; hh: PFloatArray);
  477. const
  478.    alpha = 5.0;   { Gaussian window parameter }
  479. var
  480.    i,N2: integer;
  481.    function CalcWindow(idx: integer): Float;
  482.    begin
  483.       case ord(Window) of
  484.               { Hamming }
  485.    1: Result := 0.54+0.46*cos(2*M_PI*idx/N);
  486.               { Hanning }
  487.            2: Result := 0.5+0.5*cos(2*M_PI*idx/N);
  488.               { Blackman }
  489.            3: Result := 0.42+0.5*cos(2*M_PI*idx/N)+0.08*cos(4*M_PI*idx/N);
  490.       { Gaussian }
  491.            4: Result := exp( -alpha/(N*N) * (2*(N2-idx)-N)*(2*(N2-idx)-N)   );
  492.               { Welch }
  493.            5: Result := ((2*idx-N)/N)*((2*idx-N)/(N+1));
  494.               { Parzen }
  495.            6: Result := abs((2*idx-N)/(N+1));
  496.               { Rectangular }
  497.            else Result := 1;
  498.       end;
  499.    end;
  500. begin
  501.    N2 := N div 2;
  502.    for i := 0 to N2 do
  503.    begin
  504.       hh[N2+i] := hh[N2+i] * CalcWindow(i);
  505.       hh[N2-i] := hh[N2+i];
  506.    end;
  507. end;
  508. {==============================================================================}
  509. procedure FIR_Response(FirType,N:integer; hh: PFloatArray; dBScale: Boolean;
  510.        numPoints: integer; points: PFloatArray);
  511. var
  512.    index, L, i: integer;
  513.    lambda, work: Float;
  514. begin
  515.    for L := 0 to NumPoints-1 do
  516.    begin
  517.       lambda := L*PI/NumPoints;
  518.       case FirType of
  519.    1: begin
  520.              work := hh[(N-1)div 2];
  521.  for i := 1 to ((N-1)div 2) do
  522.  begin
  523.     index := (N-1)div 2-i;
  524.     work := work+2.0*hh[index]*cos(i*lambda);
  525.                  end;
  526.               end;
  527.    2: begin
  528.                  work := 0;
  529.          for i := 1 to (N div 2) do
  530.  begin
  531.     index := N div 2-i;
  532.     work := work+2.0*hh[index]*cos((i-0.05)*lambda);
  533.                  end;
  534.               end;
  535.       end;
  536.       if dbscale then
  537.          points[L] := 20.0*log10(abs(work))
  538.       else
  539.         points[L] := abs(work);
  540.    end;
  541. end;
  542. {==============================================================================}
  543. procedure FIR_NormaliseResponse(dbscale: Boolean; NumPoints: integer; points: PFloatArray);
  544. var
  545.    i: integer;
  546.    biggest: Float;
  547. begin
  548.    if dbscale then
  549.    begin
  550.       biggest := -100.0;
  551.       for i := 0 to NumPoints-1 do
  552.           if (points[i] > biggest) then biggest := points[i];
  553.       for i := 0 to NumPoints-1 do
  554.           points[i] := ((points[i]-biggest)/100)+1;
  555.    end
  556.    else
  557.    begin
  558.       biggest := 0.0;
  559.       for i := 0 to NumPoints-1 do
  560.   if (points[i] > biggest) then biggest := points[i];
  561.       for i := 0 to NumPoints-1 do
  562.   points[i] := points[i]/biggest;
  563.    end;
  564. end;
  565. {== TMMFIRFilterItem ==========================================================}
  566. constructor TMMFIRFilterItem.Create;
  567. begin
  568.    inherited Create;
  569.    Ff1 := 0;
  570.    FGain := 0;
  571.    FOnChange := nil;
  572. end;
  573. {-- TMMFIRFilterItem ----------------------------------------------------------}
  574. constructor TMMFIRFilterItem.CreateEx(af1,aGain: Float);
  575. begin
  576.    inherited Create;
  577.    Ff1       := af1;
  578.    FGain     := aGain;
  579.    FOnChange := nil;
  580. end;
  581. {-- TMMFIRFilterItem ----------------------------------------------------------}
  582. procedure TMMFIRFilterItem.Changed;
  583. begin
  584.    if assigned(FOnChange) then FOnChange(Self);
  585. end;
  586. {-- TMMFIRFilterItem ----------------------------------------------------------}
  587. procedure TMMFIRFilterItem.SetParams(af1, aGain: Float);
  588. begin
  589.    if (af1 <> Ff1) or (aGain <> FGain) then
  590.    begin
  591.       Ff1 := af1;
  592.       FGain := aGain;
  593.       Changed;
  594.    end;
  595. end;
  596. {-- TMMFIRFilterItem ----------------------------------------------------------}
  597. procedure TMMFIRFilterItem.SetValue(index: integer; aValue: Float);
  598. var
  599.    af1,aGain: Float;
  600. begin
  601.    af1 := Ff1;
  602.    aGain := FGain;
  603.    case index of
  604.        0: af1 := aValue;
  605.        1: aGain := aValue;
  606.    end;
  607.    SetParams(af1, aGain);
  608. end;
  609. {-- TMMFIRFilterItem ----------------------------------------------------------}
  610. procedure TMMFIRFilterItem.Store(S: TStream);
  611. begin
  612.    S.WriteBuffer(Ff1,SizeOf(Ff1));
  613.    S.WriteBuffer(FGain,SizeOf(FGain));
  614. end;
  615. {-- TMMFIRFilterItem ----------------------------------------------------------}
  616. procedure TMMFIRFilterItem.Load(S: TStream);
  617. var
  618.    af1,aGain: Float;
  619. begin
  620.    S.ReadBuffer(af1,SizeOf(af1));
  621.    S.ReadBuffer(aGain,SizeOf(aGain));
  622.    SetParams(af1,aGain);
  623. end;
  624. {-- TMMFIRFilterItem ----------------------------------------------------------}
  625. procedure TMMFIRFilterItem.Assign(Source: TObject);
  626. begin
  627.    if Source is TMMFIRFilterItem then
  628.    begin
  629.       SetParams(TMMFIRFilterItem(Source).f1,
  630.                 TMMFIRFilterItem(Source).Gain);
  631.    end;
  632. end;
  633. {-- TMMFIRFilterList ----------------------------------------------------------}
  634. procedure TMMFIRFilterList.Sort;
  635. var
  636.    i,j,h: integer;
  637.    flt: TMMFIRFilterItem;
  638. begin          { Start Shell-Sort }
  639.    h := 1;
  640.    while h <= Count div 9 do h := h*3 + 1;
  641.    while h > 0 do
  642.    begin
  643.       for i := h to Count-1 do
  644.       begin
  645.          flt := Items[i];
  646.          j := i;
  647.          while (j >= h) and (Items[j-h].f1 > flt.f1) do
  648.          begin
  649.             Items[j] := Items[j-h];
  650.             dec(j, h);
  651.          end;
  652.          Items[j] := flt;
  653.       end;
  654.       h := h div 3;
  655.    end;
  656. end;
  657. {-- TMMFIRFilterList ----------------------------------------------------------}
  658. procedure TMMFIRFilterList.SetFilter(Index: integer; Filter: TMMFIRFilterItem);
  659. begin
  660.    Put(Index, Filter);
  661. end;
  662. {-- TMMFIRFilterList ----------------------------------------------------------}
  663. function TMMFIRFilterList.GetFilter(Index: integer): TMMFIRFilterItem;
  664. begin
  665.    Result := TMMFIRFilterItem(Get(Index));
  666. end;
  667. {-- TMMFIRFilterList ----------------------------------------------------------}
  668. function TMMFIRFilterList.AddObject(Item: TObject): TOLSize;
  669. begin
  670.    Result := inherited AddObject(Item);
  671.    (Item as TMMFIRFilterItem).OnChange := FFIRFilter.FilterChanged;
  672. end;
  673. {-- TMMFIRFilterList ----------------------------------------------------------}
  674. procedure TMMFIRFilterList.Assign(Source: TPersistent);
  675. var
  676.    i: integer;
  677.    Filter: TMMFIRFilterItem;
  678. begin
  679.    if (Source is TMMFIRFilterList) or (Source = nil) then
  680.    begin
  681.       BeginUpdate;
  682.       try
  683.          if (FFIRFilter <> nil) then
  684.              FFIRFilter.FUpdating := True;
  685.          FreeAll;
  686.          if (Source <> nil) then
  687.          begin
  688.             for i := 0 to TMMFIRFilterList(Source).Count-1 do
  689.             begin
  690.                Filter := TMMFIRFilterItem.Create;
  691.                Filter.Assign(TMMFIRFilterList(Source)[i]);
  692.                AddObject(Filter);
  693.             end;
  694.          end;
  695.       finally
  696.          if (FFIRFilter <> nil) then
  697.              FFIRFilter.FUpdating := False;
  698.          EndUpdate;
  699.       end;
  700.    end
  701.    else inherited assign(Source);
  702. end;
  703. {-- TMMFIRFilterList ----------------------------------------------------------}
  704. procedure TMMFIRFilterList.DefineProperties(Filer: TFiler);
  705. begin
  706.    Filer.DefineBinaryProperty('ObjectContainer', ReadData, WriteData, True);
  707. end;
  708. {-- TMMFIRFilterList ----------------------------------------------------------}
  709. procedure TMMFIRFilterList.ReadData(S: TStream);
  710. Var
  711.    pBuf: PChar;
  712.    Kennung: Longint;
  713.    ObjCount,
  714.    Index: TOLSize;
  715.    Destroy: Boolean;
  716.    Value: Longint;
  717. begin
  718.    BeginUpdate;
  719.    try
  720.       FFIRFilter.FUpdating := True;
  721.       S.ReadBuffer(Kennung,sizeOf(STREAMKENNUNG));
  722.       if (Kennung <> STREAMKENNUNG) then
  723.          raise EStreamError.Create('Invalid Object stream');
  724.       FreeAll;
  725.       { load stream items }
  726.       S.ReadBuffer(Destroy,SizeOf(Destroy));
  727.       DestroyObjects := Destroy;
  728.       { read string length }
  729.       S.ReadBuffer(Value,SizeOf(Value));
  730.       if Value > 0 then
  731.       begin
  732.          pBuf := StrAlloc(Value+1);
  733.          try
  734.             FillChar(pBuf^, Value+1, 0);
  735.             S.ReadBuffer(pBuf^, Value);
  736.             FFIRFilter.Description := StrPas(pBuf);
  737.          finally
  738.             StrDispose(pBuf);
  739.          end;
  740.       end;
  741.       S.ReadBuffer(ObjCount,SizeOf(Objcount));  { Read in Object count }
  742.       if Capacity-Count < ObjCount then Capacity := Count+ObjCount;
  743.       { Read in Object Count }
  744.       for Index := 0 to ObjCount-1 do
  745.           AddObject(ReadObjectFromStream(S));
  746.    finally
  747.       FFIRFilter.FUpdating := False;
  748.       EndUpdate;
  749.    end;
  750. end;
  751. {-- TMMFIRFilterList ----------------------------------------------------------}
  752. procedure TMMFIRFilterList.WriteData(S: TStream);
  753. var
  754.    Index,ObjCount: TOlSize;
  755.    Destroy: Boolean;
  756.    Value: Longint;
  757. begin
  758.    { Write list to Stream }
  759.    S.WriteBuffer(STREAMKENNUNG,SizeOf(STREAMKENNUNG));
  760.    Destroy := DestroyObjects;
  761.    S.WriteBuffer(Destroy,SizeOf(Destroy));
  762.    { write string length }
  763.    Value := Length(FFIRFilter.FDescription);
  764.    S.WriteBuffer(Value, SizeOf(Value));
  765.    S.WriteBuffer(PChar(FFIRFilter.FDescription)^, Length(FFIRFilter.FDescription));
  766.    ObjCount := Count;
  767.    S.WriteBuffer(ObjCount,SizeOf(ObjCount));
  768.    for Index := 0 to Count-1 do
  769.        WriteObjectToStream(Items[Index],S);
  770. end;
  771. {== TMMFIRFilter ==============================================================}
  772. constructor TMMFIRFilter.Create(aOwner: TComponent);
  773. begin
  774.    inherited Create(aOwner);
  775.    FFilters := TMMFIRFilterList.Create;
  776.    FFilters.OnChange := FiltersChanged;
  777.    FFilters.FFIRFilter := Self;
  778.    FOpen       := False;
  779.    FEnabled    := defEnabled;
  780.    FPFIR       := nil;
  781.    FPTempFIR   := nil;
  782.    FUpdating   := False;
  783.    FDescription:= 'Untitled';
  784.    Ffs         := defRate;
  785.    FWindow     := defWindow;
  786.    fnCoeffs    := defOrder;
  787.    FChannel    := defChannel;
  788.    FUpdating   := False;
  789.    FResponse   := nil;
  790.    FSpectrum   := nil;
  791.    FTempBuffer := nil;
  792.    CalcFilter;
  793.    ErrorCode := ComponentRegistered(InitCode, Self, ClassName);
  794.    if (ErrorCode <> 0) then RegisterFailed(InitCode, Self , ClassName);
  795. end;
  796. {-- TMMFIRFilter --------------------------------------------------------------}
  797. destructor TMMFIRFilter.Destroy;
  798. begin
  799.    Close;
  800.    SetResponse(nil);
  801.    SetSpectrum(nil);
  802.    FFilters.Free;
  803.    inherited
  804. end;
  805. {-- TMMFIRFilter --------------------------------------------------------------}
  806. procedure TMMFIRFilter.Notification(aComponent: TComponent; Operation: TOperation);
  807. begin
  808.    inherited Notification(aComponent, Operation);
  809.    if (Operation = opRemove) then
  810.    begin
  811.       if (aComponent = FResponse) then
  812.           FResponse := nil;
  813.       if (aComponent = FSpectrum) then
  814.           FSpectrum := nil;
  815.    end;
  816. end;
  817. {-- TMMFIRFilter --------------------------------------------------------------}
  818. procedure TMMFIRFilter.PcmOverflow;
  819. begin
  820.    if assigned(FOnPcmOverflow) then FOnPcmOverflow(Self);
  821. end;
  822. {-- TMMFIRFilter --------------------------------------------------------------}
  823. procedure TMMFIRFilter.SetCoeffs(pCoeffs: PFloatArray; nCoeffs: integer);
  824. begin
  825.    if (pCoeffs <> nil) then
  826.    begin
  827.       if (nCoeffs > MAXTAPS) then
  828.           raise EMMFIRError.Create('Only '+IntToStr(MAXTAPS)+' are allowed');
  829. //      FFirFunc := ffUser;
  830.       FnCoeffs := nCoeffs;
  831.       GlobalMoveMem(pCoeffs^,FCoeffs,nCoeffs*sizeOf(Float));
  832.       Change;
  833.    end;
  834. end;
  835. {-- TMMFIRFilter --------------------------------------------------------------}
  836. function TMMFIRFilter.GetCoeffs: PFloatArray;
  837. begin
  838.    Result := @FCoeffs;
  839. end;
  840. {-- TMMFIRFilter --------------------------------------------------------------}
  841. function TMMFIRFilter.LoadCoeffs(FName: TFileName): Boolean;
  842. var
  843.    F: TextFile;
  844.    S: string;
  845.    nCoeffs,n: integer;
  846.    Coeffs: array[0..MAXTAPS-1] of Float;
  847. begin
  848.    Result := False;
  849.    if FileExists(FName) then
  850.    try
  851.    {$I-}
  852.       AssignFile(F, FName);
  853.       Reset(F);
  854.       try
  855.          { read the number of coeffs }
  856.          ReadLn(F, S);
  857.          nCoeffs := StrToInt(S);
  858.          n := 0;
  859.          while not EOF(F) do
  860.          begin
  861.             ReadLn(F,S);
  862.             Coeffs[n] := StrToFloat(S);
  863.             inc(n);
  864.          end;
  865.          if (n = nCoeffs) then
  866.          begin
  867.             SetCoeffs(@Coeffs,nCoeffs);
  868.             Result := True;
  869.          end;
  870.       finally
  871.          CloseFile(F);
  872.       end;
  873.    except
  874.       ;
  875.    end;
  876.    {$I+}
  877. end;
  878. {-- TMMFIRFilter --------------------------------------------------------------}
  879. function TMMFIRFilter.SaveCoeffs(FName: TFileName): Boolean;
  880. var
  881.    F: TextFile;
  882.    n: integer;
  883. begin
  884.    Result := False;
  885.    try
  886.    {$I-}
  887.       AssignFile(F, FName);
  888.       Rewrite(F);
  889.       try
  890.          { write the number of coeffs }
  891.          WriteLn(F, IntToStr(FnCoeffs));
  892.          for n := 0 to FnCoeffs-1 do
  893.          begin
  894.             WriteLn(F,FloatToStr(FCoeffs[n]));
  895.          end;
  896.          Result := True;
  897.       finally
  898.          CloseFile(F);
  899.       end;
  900.    except
  901.       ;
  902.    end;
  903.    {$I+}
  904. end;
  905. {-- TMMFIRFilter --------------------------------------------------------------}
  906. procedure TMMFIRFilter.SaveToIniFile(IniFile: TFileName; Section: string);
  907. var
  908.    i: integer;
  909. begin
  910.    if (IniFile <> '') then
  911.    begin
  912.       with TIniFile.Create(IniFile) do
  913.       try
  914.          Section := 'FIR.'+Section;
  915.          WriteInteger(Section, 'Order', Order);
  916.          WriteInteger(Section, 'Window', Ord(Window));
  917.          WriteInteger(Section, 'Filters', Filters.Count);
  918.          for i := 0 to Filters.Count-1 do
  919.          with Filters[i] do
  920.          begin
  921.             WriteString(Section, 'Filter'+IntToStr(i)+' f1', FloatToStr(f1));
  922.             WriteString(Section, 'Filter'+IntToStr(i)+' Gain', FloatToStr(Gain));
  923.          end;
  924.       finally
  925.          Free;
  926.       end;
  927.    end;
  928. end;
  929. {-- TMMFIRFilter --------------------------------------------------------------}
  930. procedure TMMFIRFilter.ReadFromIniFile(IniFile: TFileName; Section: string);
  931. var
  932.    Cnt, i: integer;
  933.    f1,Gain: Float;
  934. begin
  935.    if (IniFile <> '') then
  936.    begin
  937.       with TIniFile.Create(IniFile) do
  938.       try
  939.          Section := 'FIR.'+Section;
  940.          i := ReadInteger(Section, 'Order', -1);
  941.          if (i > 0) then
  942.          begin
  943.             Filters.BeginUpdate;
  944.             try
  945.                FUpdating := True;
  946.                Filters.FreeAll;
  947.                Order       := ReadInteger(Section, 'Order', defOrder);
  948.                Window      := TMMFFTWindow(ReadInteger(Section, 'Window', Ord(defWindow)));
  949.                Description := Section;
  950.                Cnt := ReadInteger(Section, 'Filters', 0);
  951.                for i := 0 to Cnt-1 do
  952.                begin
  953.                   f1   := StrToFloat(ReadString(Section, 'Filter'+IntToStr(i)+' f1', '0'));
  954.                   Gain := StrToFloat(ReadString(Section, 'Filter'+IntToStr(i)+' Gain', '0'));
  955.                   Filters.AddObject(TMMFIRFilterItem.CreateEx(f1,Gain));
  956.                end;
  957.             finally
  958.                FUpdating := False;
  959.                Filters.EndUpdate;
  960.             end;
  961.          end;
  962.       finally
  963.          Free;
  964.       end;
  965.    end;
  966. end;
  967. {-- TMMFIRFilter --------------------------------------------------------------}
  968. procedure TMMFIRFilter.SetFilters(aValue: TMMFIRFilterList);
  969. begin
  970.    if (aValue <> FFilters) then FFilters.Assign(aValue);
  971. end;
  972. {-- TMMFIRFilter --------------------------------------------------------------}
  973. procedure TMMFIRFilter.FilterChanged(Sender: TObject);
  974. begin
  975.    if not FUpdating then
  976.    begin
  977.       CalcFilter;
  978.    end;
  979. end;
  980. {-- TMMFIRFilter --------------------------------------------------------------}
  981. procedure TMMFIRFilter.FiltersChanged(Sender: TObject);
  982. begin
  983.    if not FUpdating then
  984.    begin
  985.       CalcFilter;
  986.    end;
  987. end;
  988. {-- TMMFIRFilter --------------------------------------------------------------}
  989. procedure TMMFIRFilter.SetDescription(aValue: String);
  990. begin
  991.    if (aValue <> FDescription) then
  992.    begin
  993.       FDescription := aValue;
  994.    end;
  995. end;
  996. {-- TMMFIRFilter --------------------------------------------------------------}
  997. Procedure TMMFIRFilter.SetChannel(aValue: TMMChannel);
  998. begin
  999.    if (aValue <> FChannel) then
  1000.    begin
  1001.       FChannel := aValue;
  1002.       UpdateFilter;
  1003.    end;
  1004. end;
  1005. {-- TMMFIRFilter --------------------------------------------------------------}
  1006. procedure TMMFIRFilter.SetSampleRate(aValue: Longint);
  1007. begin
  1008.    if (aValue <> Ffs) then
  1009.    begin
  1010.       Ffs := MinMax(aValue,4000,100000);
  1011.       UpdateTempFilter(True);
  1012.       CalcFilter;
  1013.    end;
  1014. end;
  1015. {-- TMMFIRFilter --------------------------------------------------------------}
  1016. procedure TMMFIRFilter.SetWindow(aValue: TMMFFTWindow);
  1017. begin
  1018.    if (aValue <> FWindow) then
  1019.    begin
  1020.       FWindow := aValue;
  1021.       CalcFilter;
  1022.    end;
  1023. end;
  1024. {-- TMMFIRFilter --------------------------------------------------------------}
  1025. procedure TMMFIRFilter.SetNCoeffs(aValue: integer);
  1026. begin
  1027.    if (aValue <> FnCoeffs) then
  1028.    begin
  1029.       if (aValue mod 2 = 0) then inc(aValue);
  1030.       FnCoeffs := MinMax(aValue,0,MAXTAPS-1);
  1031.       CalcFilter;
  1032.    end;
  1033. end;
  1034. {-- TMMFIRFilter --------------------------------------------------------------}
  1035. procedure TMMFIRFilter.SetResponse(aValue: TMMFIRResponse);
  1036. begin
  1037.    if Longint(Self) = Longint(aValue) then exit;
  1038.    if (aValue <> FResponse) then
  1039.    begin
  1040.       if (aValue = nil) then NotifyResponse(opRemove);
  1041.       FResponse := aValue;
  1042.       NotifyResponse(opInsert);
  1043.    end;
  1044. end;
  1045. {-- TMMFIRFilter --------------------------------------------------------------}
  1046. procedure TMMFIRFilter.NotifyResponse(Operation: TOperation);
  1047. begin
  1048.    if (FResponse = nil) or
  1049.       (csLoading in ComponentState) or
  1050.       (csReading in ComponentState) then exit;
  1051.    if (Operation = opInsert) then
  1052.    begin
  1053.       FResponse.SetCoeffs(@FCoeffs,FnCoeffs);
  1054.       FResponse.SampleRate := Ffs;
  1055.    end
  1056.    else
  1057.    begin
  1058.       FResponse.SetCoeffs(nil,0);
  1059.       FResponse.SampleRate := 0;
  1060.    end;
  1061. end;
  1062. {-- TMMFIRFilter --------------------------------------------------------------}
  1063. procedure TMMFIRFilter.UpdateTempFilter(Init: Boolean);
  1064. var
  1065.    wfx: TWaveFormatEx;
  1066. begin
  1067.    DoneFIRFilter(FPTempFIR);
  1068.    if Init and (FSpectrum <> nil) then
  1069.    begin
  1070.       pcmBuildWaveHeader(@wfx, 16, 1, Ffs);
  1071.       FPTempFIR := InitFIRFilter(@wfx);
  1072.       FSpectrum.PCMWaveFormat := PPCMWaveFormat(@wfx)^;
  1073.       NotifySpectrum;
  1074.    end;
  1075. end;
  1076. {-- TMMFIRFilter --------------------------------------------------------------}
  1077. procedure TMMFIRFilter.SetSpectrum(aValue: TMMSpectrum);
  1078. begin
  1079.    if Longint(Self) = Longint(aValue) then exit;
  1080.    if (aValue <> FSpectrum) then
  1081.    begin
  1082.       if (aValue = nil) then
  1083.       begin
  1084.          FSpectrum.OnNeedData := nil;
  1085.          NotifySpectrum;
  1086.          UpdateTempFilter(False);
  1087.       end;
  1088.       FSpectrum := aValue;
  1089.       if (FSpectrum <> nil) then
  1090.       begin
  1091.          UpdateTempFilter(True);
  1092.          FSpectrum.Window := fwRectangular;
  1093.          FSpectrum.OnNeedData := SpectrumNeedData;
  1094.          NotifySpectrum;
  1095.       end;
  1096.    end;
  1097. end;
  1098. {-- TMMFIRFilter --------------------------------------------------------------}
  1099. procedure TMMFIRFilter.NotifySpectrum;
  1100. begin
  1101.    if (FSpectrum = nil) or
  1102.       (csLoading in ComponentState) or
  1103.       (csReading in ComponentState) then exit;
  1104.    FSpectrum.ResetData;
  1105. end;
  1106. {-- TMMFIRFilter --------------------------------------------------------------}
  1107. procedure TMMFIRFilter.SpectrumNeedData(Sender: TObject);
  1108. var
  1109.    wfx: TWaveFormatEx;
  1110.    BufI,BufO: array[0..8192] of Smallint;
  1111. begin
  1112.    if (Sender <> nil) then
  1113.    with TMMSpectrum(Sender) do
  1114.    begin
  1115.       pcmBuildWaveHeader(@wfx, 16, 1, Ffs);
  1116.       PCMWaveFormat := PPCMWaveFormat(@wfx)^;
  1117.       ResetFIRFilter(FPTempFIR);
  1118.       SetFIRFilter(FPTempFir, @FCoeffs, FnCoeffs, 0);
  1119.       
  1120.       GlobalFillMem(BufI,sizeOf(BufI),0);
  1121.       BufI[0] := 21500;
  1122.      
  1123.       DoFIRFilterShort(FPTempFir, @BufI, @BufO, BytesPerSpectrum);
  1124.       RefreshPCMData(@BufO);
  1125.    end;
  1126. end;
  1127. {-- TMMFIRFilter --------------------------------------------------------------}
  1128. procedure TMMFIRFilter.CalcFilter;
  1129. var
  1130.    i,Cnt: integer;
  1131.    Pts: array[0..MAXFREQS-1] of TMMFilterPoint;
  1132. begin
  1133.    if not (csLoading in ComponentState) and
  1134.       not (csReading in ComponentState) then
  1135.    begin
  1136.       Filters.Sort;
  1137.       
  1138.       Cnt := Filters.Count;
  1139.       for i := 0 to Cnt-1 do
  1140.       with Filters[i] do
  1141.       begin
  1142.          Pts[i].Freq := Ff1;
  1143.          Pts[i].Amp  := FGain;
  1144.       end;
  1145.       { make sure we have at least two valid points }
  1146.       if (Cnt = 0) then
  1147.       begin
  1148.          Pts[0].Freq := 0;
  1149.          Pts[0].Amp  := 0;
  1150.          inc(Cnt);
  1151.       end;
  1152.       if (Cnt = 1) then
  1153.       begin
  1154.          Pts[1].Freq := Ffs/2;
  1155.          Pts[1].Amp  := 0;
  1156.          inc(Cnt);
  1157.       end;
  1158.       FIR_CoeffsEx(@Pts, Cnt, Ffs, FnCoeffs, @FCoeffs);
  1159.       FIR_Window(FWindow, FnCoeffs, @FCoeffs);
  1160.       Change;
  1161.    end;
  1162. end;
  1163. {-- TMMFIRFilter --------------------------------------------------------------}
  1164. procedure TMMFIRFilter.UpdateFilter;
  1165. begin
  1166.    if FOpen and (FPFIR <> nil) then
  1167.    begin
  1168.       FCleanup := wioSamplesToBytes(PWaveFormat,FnCoeffs);
  1169.       SetFIRFilter(FPFIR, @FCoeffs, FnCoeffs, ord(FChannel));
  1170.    end;
  1171. end;
  1172. {-- TMMFIRFilter --------------------------------------------------------------}
  1173. procedure TMMFIRFilter.Change;
  1174. begin
  1175.    UpdateFilter;
  1176.    NotifyResponse(opInsert);
  1177.    NotifySpectrum;
  1178.    if assigned(FOnChange) then FOnChange(Self);
  1179. end;
  1180. {-- TMMFIRFilter --------------------------------------------------------------}
  1181. procedure TMMFIRFilter.Loaded;
  1182. begin
  1183.    inherited Loaded;
  1184.    CalcFilter;
  1185. end;
  1186. {-- TMMFIRFilter --------------------------------------------------------------}
  1187. procedure TMMFIRFilter.SetPWaveFormat(aValue: PWaveFormatEx);
  1188. begin
  1189.    if (aValue <> nil) then
  1190.    begin
  1191.       if not (csDesigning in ComponentState) then
  1192.          if not pcmIsValidFormat(aValue) or (aValue^.wBitsPerSample = 8) then
  1193.             raise EMMFIRError.Create(LoadResStr(IDS_INVALIDFORMAT));
  1194.       SampleRate := aValue^.nSamplesPerSec;
  1195.    end;
  1196.    inherited SetPWaveFormat(aValue);
  1197. end;
  1198. {-- TMMFIRFilter --------------------------------------------------------------}
  1199. procedure TMMFIRFilter.Assign(Source: TPersistent);
  1200. begin
  1201.    if (Source is TMMFIRFilter) then
  1202.    begin
  1203.       if (Source <> nil) then
  1204.       begin
  1205.          Channel    := TMMFIRFilter(Source).Channel;
  1206.          Enabled    := TMMFIRFilter(Source).Enabled;
  1207.          Description:= TMMFIRFilter(Source).Description;
  1208.          Order      := TMMFIRFilter(Source).Order;
  1209.          Window     := TMMFIRFilter(Source).Window;
  1210.          SampleRate := TMMFIRFilter(Source).SampleRate;
  1211.          Filters    := TMMFIRFilter(Source).Filters;
  1212.       end;
  1213.    end;
  1214. end;
  1215. {-- TMMFIRFilter --------------------------------------------------------------}
  1216. procedure TMMFIRFilter.Open;
  1217. begin
  1218.    if not FOpen then
  1219.    begin
  1220.       if pcmIsValidFormat(PWaveFormat) then
  1221.       begin
  1222.          FPFIR := InitFIRFilter(PWaveFormat);
  1223.          if (FPFIR = nil) then OutOfMemoryError
  1224.          else
  1225.          begin
  1226.             FTempBuffer := GlobalAllocMem(Max(QUEUE_READ_SIZE,BufferSize));
  1227.             FOpen := True;
  1228.          end;
  1229.       end;
  1230.    end;
  1231. end;
  1232. {-- TMMFIRFilter --------------------------------------------------------------}
  1233. procedure TMMFIRFilter.Start;
  1234. begin
  1235.    UpdateFilter;
  1236. end;
  1237. {-- TMMFIRFilter --------------------------------------------------------------}
  1238. procedure TMMFIRFilter.Close;
  1239. begin
  1240.    if FOpen then
  1241.    begin
  1242.       FOpen := False;
  1243.       DoneFIRFilter(FPFIR);
  1244.       GlobalFreeMem(Pointer(FTempBuffer));
  1245.    end;
  1246. end;
  1247. {-- TMMFIRFilter --------------------------------------------------------------}
  1248. procedure TMMFIRFilter.Process(Buffer: PChar; nBytes: Longint);
  1249. begin
  1250.    { process the buffer trough the delay line }
  1251.    if (FPFIR <> nil) then
  1252.    begin
  1253.       if DoFIRFilterShort(FPFIR, Buffer, FTempBuffer, nBytes) then
  1254.          GlobalSynchronize(PcmOverflow);
  1255.       GlobalMoveMem(FTempBuffer^,Buffer^,nBytes);
  1256.    end;
  1257. end;
  1258. {-- TMMFIRFilter --------------------------------------------------------------}
  1259. function TMMFIRFilter.CleanUp(Buffer: PChar; Length: integer): Longint;
  1260. begin
  1261.    { process the remaining bytes in the delay lines }
  1262.    if (FPFIR <> nil) and (FCleanup > 0) then
  1263.    begin
  1264.       FCleanup := Max(FCleanup - Length,0);
  1265.       FillChar(Buffer^, Length, 0);
  1266.       Process(Buffer, Length);
  1267.    end;
  1268.    { return the remaining bytes to process }
  1269.    Result := FCleanup;
  1270. end;
  1271. {-- TMMFIRFilter --------------------------------------------------------------}
  1272. procedure TMMFIRFilter.Opened;
  1273. begin
  1274.    inherited Opened;
  1275.    Open;
  1276. end;
  1277. {-- TMMFIRFilter --------------------------------------------------------------}
  1278. procedure TMMFIRFilter.Closed;
  1279. begin
  1280.    Close;
  1281.    inherited Closed;
  1282. end;
  1283. {-- TMMFIRFilter --------------------------------------------------------------}
  1284. procedure TMMFIRFilter.Started;
  1285. begin
  1286.    inherited Started;
  1287.    Start;
  1288. end;
  1289. {-- TMMFIRFilter --------------------------------------------------------------}
  1290. procedure TMMFIRFilter.BufferReady(lpwh: PWaveHdr);
  1291. begin
  1292.    if Enabled then
  1293.    begin
  1294.       Process(lpwh^.lpData,lpwh^.dwBytesRecorded);
  1295.    end;
  1296.    inherited BufferReady(lpwh);
  1297. end;
  1298. {-- TMMFIRFilter --------------------------------------------------------------}
  1299. procedure TMMFIRFilter.BufferLoad(lpwh: PWaveHdr; var MoreBuffers: Boolean);
  1300. var
  1301.    aLength: Longint;
  1302. begin
  1303.    inherited BufferLoad(lpwh, MoreBuffers);
  1304.    if Enabled and FOpen then
  1305.    begin
  1306.       if not MoreBuffers then
  1307.       begin
  1308.          aLength := lpwh^.dwBufferLength;
  1309.          if Cleanup(lpwh^.lpData, aLength) > 0 then MoreBuffers := True;
  1310.          lpwh^.dwBytesRecorded := aLength;
  1311.       end
  1312.       else Process(lpwh^.lpData, lpwh^.dwBytesRecorded);
  1313.    end;
  1314. end;
  1315. {==============================================================================}
  1316. {-- TMMFIRResponse                                                           --}
  1317. {==============================================================================}
  1318. constructor TMMFIRResponse.Create(aOwner: TComponent);
  1319. begin
  1320.    inherited Create(aOwner);
  1321.    Font.Style  := [fsBold];
  1322.    Ffs         := defRate;
  1323.    FnCoeffs    := 0;
  1324.    FGridColor  := defGridColor;
  1325.    FAxisColor  := defAxisColor;
  1326.    FRespColor  := defRespColor;
  1327.    FCoeffColor := defCoeffColor;
  1328.    FDBScale    := defDBScale;
  1329.    FNormalized := defNormalized;
  1330.    FShowCoeffs := defShowCoeffs;
  1331.    FScaleColor := defScaleColor;
  1332.    Color       := defColor;
  1333.    Width       := defWidth;
  1334.    Height      := defHeight;
  1335.    ErrorCode := ComponentRegistered(InitCode, Self, ClassName);
  1336.    if (ErrorCode <> 0) then RegisterFailed(InitCode, Self , ClassName);
  1337. end;
  1338. {-- TMMFIRResponse ------------------------------------------------------------}
  1339. destructor TMMFIRResponse.Destroy;
  1340. begin
  1341.    inherited Destroy;
  1342. end;
  1343. {-- TMMFIRResponse ------------------------------------------------------------}
  1344. procedure TMMFIRResponse.AdjustClientSize;
  1345. begin
  1346.    FClient := BeveledRect;
  1347.    InflateRect(FClient,-30,-30);
  1348. end;
  1349. {-- TMMFIRResponse ------------------------------------------------------------}
  1350. procedure TMMFIRResponse.SetBounds(aLeft, aTop, aWidth, aHeight: integer);
  1351. begin
  1352.    inherited SetBounds(aLeft, aTop, aWidth, aHeight);
  1353.    AdjustClientSize;
  1354. end;
  1355. {-- TMMFIRResponse ------------------------------------------------------------}
  1356. procedure TMMFIRResponse.Changed;
  1357. begin
  1358.    AdjustClientSize;
  1359.    inherited Changed;
  1360. end;
  1361. {-- TMMFIRResponse ------------------------------------------------------------}
  1362. procedure TMMFIRResponse.SetCoeffs(pCoeffs: PFloatArray; nCoeffs: integer);
  1363. begin
  1364.    FnCoeffs := nCoeffs;
  1365.    if (FnCoeffs > 0) then
  1366.    begin
  1367.       GlobalMoveMem(pCoeffs^,FCoeffs,FnCoeffs*sizeOf(Float));
  1368.    end;
  1369.    Invalidate;
  1370. end;
  1371. {-- TMMFIRResponse ------------------------------------------------------------}
  1372. procedure TMMFIRResponse.SetBoolean(index: integer; aValue: Boolean);
  1373. begin
  1374.    case index of
  1375.       0: if (FShowCoeffs = aValue) then exit else FShowCoeffs := aValue;
  1376.       1: if (FDBScale = aValue) then exit else FDBScale := aValue;
  1377.       2: if (FNormalized = aValue) then exit else FNormalized := aValue;
  1378.    end;
  1379.    Invalidate;
  1380. end;
  1381. {-- TMMFIRResponse ------------------------------------------------------------}
  1382. procedure TMMFIRResponse.SetSampleRate(aValue: Longint);
  1383. begin
  1384.    if (aValue <> Ffs) then
  1385.    begin
  1386.       Ffs := MinMax(aValue,4000,100000);
  1387.       Invalidate;
  1388.    end;
  1389. end;
  1390. {-- TMMFIRResponse ------------------------------------------------------------}
  1391. procedure TMMFIRResponse.SetColors(index: integer; aValue: TColor);
  1392. begin
  1393.    case index of
  1394.        0: if (aValue = FGridColor) then exit else FGridColor := aValue;
  1395.        1: if (aValue = FAxisColor) then exit else FAxisColor := aValue;
  1396.        2: if (aValue = FRespColor) then exit else FRespColor := aValue;
  1397.        3: if (aValue = FCoeffColor) then exit else FCoeffColor := aValue;
  1398.        4: if (aValue = FScaleColor) then exit else FScaleColor := aValue;
  1399.    end;
  1400.    Invalidate;
  1401. end;
  1402. {-- TMMFIRResponse ------------------------------------------------------------}
  1403. procedure TMMFIRResponse.VLineDoted(aCanvas:TCanvas;x,y1,y2:integer;Clr:TColorRef);
  1404. var
  1405.    DC: HDC;
  1406. begin
  1407.    DC := aCanvas.Handle;
  1408.    if (y1 > y2) then SwapInt(y1,y2);
  1409.    while y1 < y2 do
  1410.    begin
  1411.       SetPixel(DC,x,y1,Clr);
  1412.       inc(y1,2);
  1413.    end;
  1414. end;
  1415. {-- TMMFIRResponse ------------------------------------------------------------}
  1416. procedure TMMFIRResponse.HLineDoted(aCanvas:TCanvas;x1,x2,y:integer;Clr:TColorRef);
  1417. var
  1418.    DC: HDC;
  1419. begin
  1420.    DC := aCanvas.Handle;
  1421.    if (x1 > x2) then SwapInt(x1,x2);
  1422.    while x1 < x2 do
  1423.    begin
  1424.       SetPixel(DC,x1,y,Clr);
  1425.       inc(x1,2);
  1426.    end;
  1427. end;
  1428. {-- TMMFIRResponse ------------------------------------------------------------}
  1429. procedure TMMFIRResponse.DrawBackground(Canvas: TCanvas; Client: TRect);
  1430. var
  1431.    i,x,y: integer;
  1432.    str: string;
  1433.    th,tw: integer;
  1434.    W,H: integer;
  1435.    Clr: Longint;
  1436.    NGrids: integer;
  1437. begin
  1438.    with Canvas,Client do
  1439.    begin
  1440.       W:= Right-Left;
  1441.       H:= Bottom-Top;
  1442.       Pen.Color := FAxisColor;
  1443.       Font.Color := FScaleColor;
  1444.       MoveTo(Left,Top);
  1445.       LineTo(Left,Bottom);
  1446.       LineTo(Right,Bottom);
  1447.       Font := Self.Font;
  1448.       SetTextAlign(Handle,TA_BASELINE or TA_CENTER);
  1449. (*
  1450.       case FFirFunc of
  1451.           ffLowPass : str := Format('Low Pass:';
  1452.           ffHighPass: str := Format('High Pass:';
  1453.           ffBandPass: str := Format('Band Pass:';
  1454.           ffBandStop: str := Format('Band Stop: f1 %d, f2 %d, %d taps',[;
  1455.           ffUser    : str := Format('User: f1 %d, f2 %d';
  1456.       str := Format(str+' Sample rate %d, f1 %d, %d taps',
  1457.                     [Ffs,0,FnCoeffs]);
  1458.   *)
  1459.       str := Format('FIR Filter Response: %f Khz, %d taps',[Ffs/1000,FnCoeffs]);
  1460.       TextOut(Left+W div 2,Top-10,str);
  1461.       Font.Handle:= CreateFont(-9,0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,
  1462.                                OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,
  1463.                                VARIABLE_PITCH or FF_SWISS,'arial');
  1464.       SetTextAlign(Handle,TA_RIGHT);
  1465.       Clr := ColorToRGB(GridColor);
  1466.       th := TextHeight('W');
  1467.       if FNormalized then
  1468.          tw := TextWidth('0.5')
  1469.       else
  1470.          tw := TextWidth(' '+IntToStr(Ffs div 2));
  1471.       { calc the number of steps required }
  1472.       NGrids := 20;
  1473.       while (H div NGrids < th) or
  1474.             (W div NGrids < tw) do
  1475.       begin
  1476.          NGrids := NGrids div 2;
  1477.          if NGrids = 1 then break;
  1478.       end;
  1479.       for i := 0 to NGrids do
  1480.       begin
  1481.          y := (H*i)div NGrids;
  1482.       MoveTo(Left, Top+y);
  1483.          LineTo(Left-5, Top+y);
  1484.          if (i <> NGrids) then
  1485.             HLineDoted(Canvas,Left+1,Left+W,Top+y,Clr);
  1486.          if FDBScale then
  1487.          begin
  1488.     if (i = 0) then
  1489.        str := '0'
  1490.             else
  1491.        str := Format('-%d',[(i*100)div NGrids]);
  1492.          end
  1493.          else str := Format('%d%',[100-(i*100)div NGrids]);
  1494.          TextOut(Left-7,Top+y-(th div 2),str);
  1495.       end;
  1496.       if FDBScale then
  1497.          TextOut(Left-8,Bottom+10,'dB')
  1498.       else
  1499.          TextOut(Left-7,Bottom+10,'%');
  1500.       // mark the frequency scale (linear as a function of sample frequency)
  1501.       SetTextAlign(Handle,TA_CENTER or TA_TOP);
  1502.       for i := 0 to NGrids do
  1503.       begin
  1504.          x := (i*W) div NGrids;
  1505.          MoveTo(Left+x,Bottom);
  1506.          LineTo(Left+x,Bottom+5);
  1507.          if (i > 0) then
  1508.             VLineDoted(Canvas,Left+x,Top,Top+H,Clr);
  1509.          if FNormalized then
  1510.             str := Format('%f',[(0.5*i)/NGrids])
  1511.          else
  1512.             str := Format('%d',[(Ffs div 2)*i div NGrids]);
  1513.          TextOut(Left+x,Bottom+10,str);
  1514.       end;
  1515.       if not FNormalized then
  1516.          TextOut(Left+x+(tw div 2)+5,Bottom+10,' Hz');
  1517.    end;
  1518. end;
  1519. {-- TMMFIRResponse ------------------------------------------------------------}
  1520. procedure TMMFIRResponse.DrawImpulseResponse(Canvas: TCanvas; Client: TRect);
  1521. const
  1522.      HS = 4;
  1523. var
  1524.    W,H,i,ftype: integer;
  1525.    coeffs: array[0..4096] of Float;
  1526. begin
  1527.    if (FnCoeffs > 0) then
  1528.    with Canvas,Client do
  1529.    begin
  1530.       if (FnCoeffs and 1 <> 0) then
  1531.           ftype := 1
  1532.       else
  1533.           ftype := 2;
  1534.       H := Bottom-Top;
  1535.       W := Right-Left;
  1536.       FIR_Response(ftype,FnCoeffs,@FCoeffs,FDBScale,W,@coeffs);
  1537.       FIR_NormaliseResponse(FDBScale,W,@coeffs);
  1538.       Pen.Color := FRespColor;
  1539.       MoveTo(Left,Bottom-Trunc(coeffs[0]*H));
  1540.       for i := 0 to W-1 do
  1541.       begin
  1542.          LineTo(Left+i,Bottom-Trunc(coeffs[i]*H));
  1543.       end;
  1544.       if FShowCoeffs then
  1545.       begin
  1546.          // last thing: draw the filter itself
  1547.          Pen.Color := FCoeffColor;
  1548.          MoveTo(Left,Bottom-Trunc(FCoeffs[0]));
  1549.          if (FnCoeffs > 1) then
  1550.          for i := 0 to FnCoeffs-1 do
  1551.          begin
  1552.             LineTo(Left+(W*i)div (FnCoeffs-1),
  1553.                    Bottom-Trunc(FCoeffs[i]*H));
  1554.          end;
  1555.       end;
  1556.    end;
  1557. end;
  1558. {-- TMMFIRResponse ------------------------------------------------------------}
  1559. procedure TMMFIRResponse.Paint;
  1560. begin
  1561.    inherited Paint;
  1562.    DrawBackground(Canvas,FClient);
  1563.    DrawImpulseResponse(Canvas,FClient);
  1564. end;
  1565. Initialization
  1566.    { register filter class for streaming ! }
  1567.    DoRegisterClass(@TMMFIRFilterItem.Load,
  1568.                    @TMMFIRFilterItem.Store,
  1569.                     TMMFIRFilterItem);
  1570. end.