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

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/index.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: 19.11.98 - 22:31:13 $                                        =}
  24. {========================================================================}
  25. Unit MMSpectr;
  26. {$C FIXED PRELOAD PERMANENT}
  27. {$I COMPILER.INC}
  28. interface
  29. uses
  30. {$IFDEF WIN32}
  31.     Windows,
  32. {$ELSE}
  33.     WinTypes,
  34.     WinProcs,
  35. {$ENDIF}
  36.     SysUtils,
  37.     Messages,
  38.     Classes,
  39.     Graphics,
  40.     Controls,
  41.     Forms,
  42.     Menus,
  43.     MMSystem,
  44.     MMUtils,
  45.     MMObj,
  46.     MMTimer,
  47.     MMString,
  48.     MMMath,
  49.     MMMulDiv,
  50.     MMFFT,
  51.     MMRegs,
  52.     MMPCMSup,
  53.     MMDIBCv;
  54. const
  55.     {$IFDEF CBUILDER3} {$EXTERNALSYM SCALEHEIGHT} {$ENDIF}
  56.     SCALEHEIGHT     = 40;
  57.     {$IFDEF CBUILDER3} {$EXTERNALSYM SCALEWIDTH} {$ENDIF}
  58.     SCALEWIDTH      = 32;
  59.     {$IFDEF CBUILDER3} {$EXTERNALSYM SCALEFONT} {$ENDIF}
  60.     SCALEFONT       = 'ARIAL';
  61.     SCALEFONTSIZE   : integer = 10;
  62.     SCROLLDISTANCE  : integer = 2;
  63.     INFOCOLOR       : TCOLOR = clWhite;
  64.     {$IFDEF CBUILDER3} {$EXTERNALSYM MAX_FFTLEN} {$ENDIF}
  65.     MAX_FFTLEN      = 4096; { Define the maximum FFT buffer length.        }
  66.     {$IFDEF CBUILDER3} {$EXTERNALSYM MAXDECAYCOUNT} {$ENDIF}
  67.     MAXDECAYCOUNT   = 32;   { Maximum amount of temporal averaging allowed }
  68. type
  69.     EMMSpectrumError     = class(Exception);
  70.     TMMSpectrumKind      = (skDots, skLines, skVLines, skBars, skPeaks, skScroll);
  71.     TMMSpectrumGain      = (sgNone,sg3db,sg6db,sg9db,sg12db);
  72.     TMMSpectrumDrawBar   = procedure(Sender: TObject; DIB: TMMDIBCanvas; Rect: TRect; nSpots,Peak: integer) of object;
  73.     TMMSpectrumClear     = procedure(Sender: TObject; DIB: TMMDIBCanvas; Rect: TRect) of object;
  74.     TMMSpectrumGetXScale = procedure(Sender: TObject; pX1,pX2: PIntArray) of object;
  75.     { array for uniform decay mode values }
  76.     PDataBuf       = ^TDataBuf;
  77.     TDataBuf       = array[0..MAXDECAYCOUNT-1] of PLongArray;
  78.     TPeak     = record          { record for peak values                  }
  79.       Freq     : Float;
  80.       Amp      : Float;
  81.       db       : Float;
  82.       { !! internal for peak display, do not use !!                }
  83.       Amplitude: Long;          { peak amplitude found             }
  84.       Index    : integer;       { bin number of the peak amplitude }
  85.       X        : integer;       { the X value for the Peak         }
  86.     end;
  87.     TDrawVal = record           { record for display values to draw    }
  88.       Left     : integer;       { left X1 for this set of bin's        }
  89.       Right    : integer;       { right X2 for this set of bin's       }
  90.       Value    : Longint;       { the (Y) value for this set of bin's  }
  91.       Peak     : integer;       { the peak value for this set of bin's }
  92.       PeakCnt  : integer;       { internal peak counter for timing     }
  93.     end;
  94.     PDrawArray = ^TDrawArray;
  95.     TDrawArray = array[0..DebugCount] of TDrawVal;
  96.     {-- TMMSpectrum -----------------------------------------------------}
  97.     TMMSpectrum = class(TMMDIBGraphicControl)
  98.     private
  99.       FTimerID    : Longint;    { timer for peak handling                }
  100.       FBarDIB     : TMMDIBCanvas;{ bitmap for inactive bars              }
  101.       {$IFDEF WIN32}
  102.       FpFFT       : PFFTReal;   { the instance for the FFT               }
  103.       {$ELSE}
  104.       FFT         : TMMFFT;     { the object that performs the FFT       }
  105.       {$ENDIF}
  106.       FFFTData    : PSmallArray;{ Array for FFT data                     }
  107.       FWinBuf     : PIntArray;  { Array storing windowing function       }
  108.       FDataBuf    : PDataBuf;   { Memory for averaging mode              }
  109.       FYBase      : PLongArray; { Scaling offset for log calculations    }
  110.       FLastVal_F  : PFloatArray;{ Last value buffer for exp decay mode   }
  111.       FLastVal    : PLongArray; { Last value buffer for uniform averaging}
  112.       FDisplayVal : PLongArray; { Array storing display values           }
  113.       Fx1         : PIntArray;  { Array of bin #'s displayed             }
  114.       Fx2         : PIntArray;  { Array of terminal bin #'s              }
  115.       FYScale     : PIntArray;  { scaling factors                        }
  116.       FDrawVal    : PDrawArray; { array with the rect's / points to draw }
  117.       FFTLen      : integer;    { Number of points for FFT               }
  118.       FSampleRate : Longint;    { A/D sampling rate                      }
  119.       FLogFreq    : Boolean;    { true for log-based frequency scale     }
  120.       FLogAmp     : Boolean;    { true for log-based amplitude scale     }
  121.       Fys         : Float;      { set for max of y-axis                  }
  122.       FLogBase    : integer;    { base of log scale (default=6 = -60db)  }
  123.       FLogs       : integer;    { for max of log scale (default=0 = 0db) }
  124.       FGain3db    : integer;    { indicating 3db/octave scale factor gain}
  125.       FDeriv      : integer;    { doing differencing for 6db/octave gain }
  126.       FRefFreq    : integer;    { ref. frequency for n db/octave gains   }
  127.       FPeak         : TPeak;    { the current peak value over all frequ. }
  128.       FWindow       : TMMFFTWindow;{ selected window function            }
  129.       FDecay        : integer;  { the current Decay value                }
  130.       FDecayMode    : TMMDecayMode;{ indicating decay mode on/off        }
  131.       FDecayFactor  : Float;    { Geometric decay factor                 }
  132.       FDecayCount   : integer;  { Temporal averaging parameter           }
  133.       FDecayCntAct  : integer;  { Total number of bins averaged so far   }
  134.       FMaxDecayCount: integer;  { Maximum value for the decay count      }
  135.       FDecayPtr     : integer;  { index for cur. averag. buffer location }
  136.       FShift           : integer;{ Number of bits for gain shift         }
  137.       FLogScaleFactor  : Float;  { Scaling factor for log values         }
  138.       FDispScaleFactor : Float;  { Display scalefactor for log values    }
  139.       FFreqScaleFactor : Float;  { Scalefactor for inc. the horiz. scale }
  140.       FFreqBase        : Float;  { Base frequency for the display        }
  141.       FKind          : TMMSpectrumKind;{ draw as dots,bars,lines,vlines  }
  142.       FEnabled       : Boolean;  { Enable or disable Spectrum            }
  143.       FBar1Color     : TColor;   { Farbe f黵 die Punkte im 1. Abschnitt  }
  144.       FBar2Color     : TColor;   { Farbe f黵 die Punkte im 2. Abschnitt  }
  145.       FBar3Color     : TColor;   { Farbe f黵 die Punkte im 3. Abschnitt  }
  146.       FInact1Color   : TColor;   { foreColor for inactive spots 1        }
  147.       FInact2Color   : TColor;   { foreColor for inactive spots 2        }
  148.       FInact3Color   : TColor;   { foreColor for inactive spots 3        }
  149.       FScaleTextColor: TColor;   { the text color for the scale          }
  150.       FScaleLineColor: TColor;   { the line color for the scale          }
  151.       FGridColor     : TColor;   { the grid color                        }
  152.       FScaleBackColor: TColor;   { background color for the scale        }
  153.       FInactiveDoted : Boolean;  { draw the inactive spots doted         }
  154.       FActiveDoted   : Boolean;  { draw the active spots doted           }
  155.       FPoint1        : integer;  { Schwelle von 1. zu 2. Abschnitt %     }
  156.       FPoint2        : integer;  { Schwelle von 2. zu 3. Abschnitt %     }
  157.       FPoint1Spot    : integer;  { on which spot begins next color       }
  158.       FPoint2Spot    : integer;  { on which spot begins next color       }
  159.       FSpotSpace     : integer;  { vertical space between spots          }
  160.       FSpotHeight    : integer;  { the spot height in pixel              }
  161.       FSpace         : integer;  { horizontal between the bars           }
  162.       FFirstSpace    : integer;  { the space before the first spot       }
  163.       FNumSpots      : integer;  { number of Spots                       }
  164.       FNumPeaks      : integer;  { number of spots displayed as peak     }
  165.       FPeakDelay     : integer;  { the delay for the peak spots          }
  166.       FPeakSpeed     : integer;  { the decrease speed for the peak spots }
  167.       FDisplayPeak   : Boolean;  { show the highest frequency or not     }
  168.       FDrawInactive  : Boolean;  { draw the inactive spots or not        }
  169.       FBits          : TMMBits;  { bit8 or bit16                         }
  170.       FChannel       : TMMChannel;{ chBoth, chLeft or chRigth            }
  171.       FMode          : TMMMode;  { mMono, mStereo or mQuadro             }
  172.       FBytes         : Longint;  { calculated data bytes per spectrum    }
  173.       FGain          : TMMSpectrumGain;{ Amount of db/octave gain        }
  174.       FOldShowHint   : Boolean;  { saved ShowHint propertie              }
  175.       FShowInfo      : Boolean;  { show the freq/amp info or not         }
  176.       FShowInfoHint  : Boolean;  { mouse is down, show the info          }
  177.       FDrawFreqScale : Boolean;  { draw the horiz scale or not          }
  178.       FDrawAmpScale  : Boolean;  { draw the vert scale or not            }
  179.       FDrawGrid      : Boolean;  { draw the grid or not                  }
  180.       FWidth         : integer;  { calculated width without border       }
  181.       FHeight        : integer;  { calculated height without border      }
  182.       FClientRect    : TRect;    { calculated beveled Rect               }
  183.       { Events }
  184.       FOnNeedData       : TNotifyEvent;
  185.       FOnGainOverflow   : TNotifyEvent;
  186.       FOnPcmOverflow    : TNotifyEvent;
  187.       FOnDrawBar        : TMMSpectrumDrawBar;
  188.       FOnClearBackground: TMMSpectrumClear;
  189.       FOnGetXScale      : TMMSpectrumGetXScale;
  190.       procedure CreateDataBuffers(Length: integer);
  191.       procedure FreeDataBuffers;
  192.       procedure CreateArrays(Size: integer);
  193.       procedure FreeArrays;
  194.       procedure ResetDecayBuffers;
  195.       procedure ResetPeakValues;
  196.       procedure XRangeCheck;
  197.       procedure SetupXScale;
  198.       procedure SetupLogScales;
  199.       procedure SetupLinScales;
  200.       procedure CalcNumSpots;
  201.       procedure CalcMagnitude(MagnitudeForm: Boolean);
  202.       procedure CalcDisplayValues;
  203.       procedure SetBytesPerSpectrum;
  204.       procedure InitializeData;
  205.       procedure NeedData;
  206.       procedure DrawFrequencyScale(Dummy: Boolean);
  207.       procedure DrawAmplitudeScale;
  208.       procedure SetLocalVariables(DIB: TMMDIBCanvas);
  209.       procedure InitLocalVariables;
  210.       procedure DrawPeakValue;
  211.       {$IFDEF USEASM}
  212.       procedure DrawBar(X1,X2,nSpots,Peak: integer);{$IFDEF WIN32}pascal;{$ENDIF}
  213.       procedure DrawBarPeak(X1,X2,nSpots,Peak: integer);{$IFDEF WIN32}pascal;{$ENDIF}
  214.       procedure PointedLineTo(X,Y: integer; Pointed: Boolean);{$IFDEF WIN32}pascal;{$ENDIF}
  215.       {$ENDIF}
  216.       procedure DrawBar_Native(X1,X2,nSpots,Peak: integer);
  217.       procedure DrawBarPeak_Native(X1,X2,nSpots,Peak: integer);
  218.       procedure DrawGrids;
  219.       procedure DrawInfo(Pos: TPoint);
  220.       procedure DrawAsDots;
  221.       procedure DrawAsLines;
  222.       procedure DrawAsVLines;
  223.       procedure DrawAsBars;
  224.       procedure DrawInactiveSpots;
  225.       procedure DrawSpectrum(Clear: Boolean);
  226.       procedure SetOnDrawBar(aValue: TMMSpectrumDrawBar);
  227.       procedure AdjustSize(var W, H: Integer);
  228.       procedure AdjustBounds;
  229.       procedure SetFFTLen(aLength: integer);
  230.       procedure SetWindow(aValue: TMMFFTWindow);
  231.       procedure SetLogFreq(aValue: Boolean);
  232.       procedure SetLogAmp(aValue: Boolean);
  233.       procedure SetKind(aValue: TMMSpectrumKind);
  234.       procedure SetDecayMode(aValue: TMMDecayMode);
  235.       procedure SetDecay(aValue: integer);
  236.       procedure SetVertScale(aValue: integer);
  237.       function  GetVertScale: integer;
  238.       procedure SetFreqScale(aValue: integer);
  239.       function  GetFreqScale: integer;
  240.       procedure SetDrawFreqScale(aValue: Boolean);
  241.       procedure SetDrawAmpScale(aValue: Boolean);
  242.       procedure SetDrawGrid(aValue: Boolean);
  243.       procedure SetEnabled(aValue: Boolean);
  244.       procedure SetColors(Index: Integer; Value: TColor);
  245.       procedure SetPoints(Index, aValue: integer);
  246.       procedure SetSpotSpace(aValue: integer);
  247.       procedure SetSpotHeight(aValue: integer);
  248.       procedure SetSpace(aValue: integer);
  249.       procedure SetNumPeaks(aValue: integer);
  250.       procedure SetPeakDelay(aValue: integer);
  251.       procedure SetPeakSpeed(aValue: integer);
  252.       procedure SetDisplayPeak(aValue: Boolean);
  253.       procedure SetPCMWaveFormat(wf: TPCMWaveFormat);
  254.       function  GetPCMWaveFormat: TPCMWaveFormat;
  255.       procedure SetBits(aValue: TMMBits);
  256.       procedure SetChannel(aValue: TMMChannel);
  257.       procedure SetMode(aValue: TMMMode);
  258.       procedure SetSampleRate(aValue: Longint);
  259.       procedure SetRefFreq(aValue: integer);
  260.       procedure SetGain(aValue: TMMSpectrumGain);
  261.       procedure SetDrawInactive(aValue: Boolean);
  262.       procedure SetInactiveDoted(aValue: Boolean);
  263.       procedure SetActiveDoted(aValue: Boolean);
  264.       function  GetScaleBackColor: TColor;
  265.       function  GetPeak: TPeak;
  266.       procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED;
  267.     protected
  268.       procedure ChangeDesigning(aValue: Boolean); override;
  269.       procedure SetBPP(aValue: integer); override;
  270.       procedure Paint; override;
  271.       procedure Loaded; override;
  272.       procedure GainOverflow; dynamic;
  273.       procedure PcmOverflow; dynamic;
  274.       procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); override;
  275.       procedure Changed; override;
  276.       procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  277.       procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  278.       procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  279.     public
  280.       constructor Create(AOwner: TComponent); override;
  281.       destructor  Destroy; override;
  282.       function  GetOptimalWidth(aWidth: integer): integer;
  283.       procedure ForceRescale;
  284.       function  GetFrequencyAtPos(Pos: TPoint): Float;
  285.       function  GetAmplitudeAtPos(Pos: TPoint): Float;
  286.       procedure RefreshPCMData(PCMData: Pointer);
  287.       procedure RefreshFFTData(FFTData: Pointer);
  288.       procedure RefreshMagnitudeData(MagData: Pointer);
  289.       procedure ResetData;
  290.       property  Peak: TPeak read GetPeak;
  291.       property  BytesPerSpectrum: Longint read FBytes;
  292.       property  PCMWaveFormat: TPCMWaveFormat read GetPCMWaveFormat write SetPCMWaveFormat;
  293.       property  FFTData: PSmallArray read FFFTData;
  294.     published
  295.       { Events }
  296.       property OnClick;
  297.       property OnDblClick;
  298.       property OnMouseDown;
  299.       property OnMouseMove;
  300.       property OnMouseUp;
  301.       property OnDragDrop;
  302.       property OnDragOver;
  303.       property OnEndDrag;
  304.       property OnStartDrag;
  305.       property OnGetXScale: TMMSpectrumGetXScale read FOnGetXScale write FOnGetXScale;
  306.       property OnNeedData: TNotifyEvent read FOnNeedData write FOnNeedData;
  307.       property OnDrawBar: TMMSpectrumDrawBar read FOnDrawBar write SetOnDrawBar;
  308.       property OnClearBackground: TMMSpectrumClear read FOnClearBackground write FOnClearBackground;
  309.       property OnGainOverflow: TNotifyEvent read FOnGainOverflow write FOnGainOverflow;
  310.       property OnPcmOverflow: TNotifyEvent read FOnPcmOverflow write FOnPcmOverflow;
  311.       property Align;
  312.       property Bevel;
  313.       property BackGroundDIB;
  314.       property UseBackGroundDIB;
  315.       property PaletteRealize;
  316.       property Color default clBlack;
  317.       property Cursor default crCross;
  318.       property ParentShowHint;
  319.       property ParentColor default False;
  320.       property PopupMenu;
  321.       property Visible;
  322.       property ShowHint;
  323.       property ShowInfo: Boolean read FShowInfo write FShowInfo default True;
  324.       property Enabled: Boolean read FEnabled write SetEnabled default True;
  325.       property DrawFreqScale: Boolean read FDrawFreqScale write SetDrawFreqScale default False;
  326.       property DrawAmpScale: Boolean read FDrawAmpScale write SetDrawAmpScale default False;
  327.       property DrawGrid: Boolean read FDrawGrid write SetDrawGrid default False;
  328.       property Height default 89;
  329.       property Width default 194;
  330.       property Space: integer read FSpace write SetSpace default 1;
  331.       property SpotSpace: integer read FSpotSpace write SetSpotSpace default 1;
  332.       property SpotHeight: integer read FSpotHeight write SetSpotHeight default 1;
  333.       property Bar1Color: TColor index 0 read FBar1Color write SetColors default clAqua;
  334.       property Bar2Color: TColor index 1 read FBar2Color write SetColors default clAqua;
  335.       property Bar3Color: TColor index 2 read FBar3Color write SetColors default clRed;
  336.       property Inactive1Color: TColor index 3 read FInact1Color write SetColors default clTeal;
  337.       property Inactive2Color: TColor index 4 read FInact2Color write SetColors default clTeal;
  338.       property Inactive3Color: TColor index 5 read FInact3Color write SetColors default clMaroon;
  339.       property ScaleTextColor: TColor index 6 read FScaleTextColor write SetColors default clBlack;
  340.       property ScaleLineColor: TColor index 7 read FScaleLineColor write SetColors default clBlack;
  341.       property GridColor: TColor index 8 read FGridColor write SetColors default clGray;
  342.       {$IFDEF BUILD_ACTIVEX}
  343.       property ScaleBackColor: TColor index 9 read FScaleBackColor write SetColors default clBtnFace;
  344.       {$ENDIF}
  345.       property Point1: integer index 0 read FPoint1 write SetPoints default 50;
  346.       property Point2: integer index 1 read FPoint2 write SetPoints default 85;
  347.       property DrawInactive: Boolean read FDrawInactive write SetDrawInactive default True;
  348.       property InactiveDoted: Boolean read FInactiveDoted write SetInactiveDoted default False;
  349.       property ActiveDoted: Boolean read FActiveDoted write SetActiveDoted default False;
  350.       property Mode: TMMMode read FMode write SetMode default mMono;
  351.       property BitLength: TMMBits read FBits write SetBits default b8bit;
  352.       property Channel: TMMChannel read FChannel write SetChannel default chBoth;
  353.       property SampleRate: Longint read FSampleRate write SetSampleRate default 11025;
  354.       property RefFreq: integer read FRefFreq write SetRefFreq default 1000;
  355.       property Gain: TMMSpectrumGain read FGain write SetGain default sgNone;
  356.       property FFTLength: integer read FFTLen write SetFFTLen default 128;
  357.       property LogFreq: Boolean read FLogFreq write SetLogFreq default False;
  358.       property LogAmp: Boolean read FLogAmp write SetLogAmp default False;
  359.       property Kind: TMMSpectrumKind read FKind write SetKind default skBars;
  360.       property Window: TMMFFTWindow read FWindow write SetWindow default fwHamming;
  361.       property DecayMode: TMMDecayMode read FDecayMode write SetDecayMode default dmNone;
  362.       property Decay: integer read FDecay write SetDecay default 1;
  363.       property VerticalScale: integer read GetVertScale write SetVertScale default 100;
  364.       property FrequencyScale: integer read GetFreqScale write SetFreqScale default 1;
  365.       property NumPeaks: integer read FNumPeaks write SetNumPeaks default 1;
  366.       property PeakDelay: integer read FPeakDelay write SetPeakDelay default 20;
  367.       property PeakSpeed: integer read FPeakSpeed write SetPeakSpeed default 0;
  368.       property DisplayPeak: Boolean read FDisplayPeak write SetDisplayPeak default False;
  369.     end;
  370. implementation
  371. uses Consts;
  372. {.$DEFINE USE_INTEGER_CODE}
  373. {$IFDEF USE_INTEGER_CODE}
  374. const
  375.     { Table for approximating the logarithm.
  376.     { These values are round(log2(index/16)*8192) for index=0:31 }
  377.     _ln: array[0..31] of Long = (-131072,-32768,-24576,-19784,-16384,
  378.                                  -13747,-11592,-9770,-8192,-6800,-5555,
  379.                                  -4428,-3400,-2454,-1578,-763,0,716,1392,
  380.                                  2031,2637,3214,3764,4289,4792,5274,5738,
  381.                                  6184,6614,7029,7429,7817);
  382. {$ENDIF}
  383. var
  384.    { local variables for fast asm drawing }
  385.    _DIB          : TMMDIBCanvas;
  386.    _DIB_ORIENT   : integer;
  387.    _biBits       : Longint;
  388.    _biBPP        : Longint;
  389.    _biWidth      : Longint;
  390.    _biHeight     : Longint;
  391.    _biScanWidth  : Longint;
  392.    _biLineDiff   : Longint;
  393.    _biColor      : Longint;
  394.    _biSurface    : Pointer;
  395.    _biPenPos     : TPoint;
  396.    _biClipRect   : TRect;
  397.    _Bar1Color    : Cardinal;
  398.    _Bar2Color    : Cardinal;
  399.    _Bar3Color    : Cardinal;
  400.    _Inact1Color  : Cardinal;
  401.    _Inact2Color  : Cardinal;
  402.    _Inact3Color  : Cardinal;
  403.    _NumSpots     : integer;
  404.    _NumPeaks     : integer;
  405.    _SpotHeight   : Longint;
  406.    _SpotSpace    : Longint;
  407.    _FirstSpace   : Longint;
  408.    _Space        : Longint;
  409.    _Point1Spot   : integer;
  410.    _Point2Spot   : integer;
  411.    _ActiveDoted  : Boolean;
  412.    _InactiveDoted: Boolean;
  413.    _DrawInactive : Boolean;
  414.    _Offset       : integer;
  415. const
  416.    SaveDC        : HDC     = 0;
  417.    SaveBitmap    : HBitmap = 0;
  418.    SaveWidth     : integer = 0;
  419.    SaveHeight    : integer = 0;
  420.    SaveInfoPos   : TPoint  = (X:0;Y:0);
  421.    OldBitmap     : HBitmap = 0;
  422. {------------------------------------------------------------------------}
  423. procedure TimeCallBack(uTimerID, dwUser: Longint);export;
  424. var
  425.    j: integer;
  426. begin
  427.   if (dwUser <> 0) then
  428.   with TMMSpectrum(dwUser) do
  429.   begin
  430.      if (FNumPeaks < 1) or (FDrawVal = nil) or FShowInfoHint then exit;
  431.      j := 0;
  432.      while (FDrawVal^[j].Left <> -1) and (j < FWidth) do
  433.      with FDrawVal^[j] do
  434.      begin
  435.         if (Peak > 0) then
  436.         begin
  437.            dec(PeakCnt);
  438.            if PeakCnt <= 0 then
  439.            begin
  440.               if (FPeakSpeed = 0) then
  441.               begin
  442.                  Peak := 0;                 { clear the peak hold spot }
  443.                  PeakCnt := 0;
  444.               end
  445.               else
  446.               begin
  447.                  dec(Peak);                       { dec the peak spot }
  448.                  PeakCnt := FPeakSpeed;
  449.               end;
  450.            end;
  451.         end;
  452.         inc(j);
  453.      end;
  454.   end;
  455. end;
  456. {-- TMMSpectrum ---------------------------------------------------------}
  457. constructor TMMSpectrum.Create(AOwner: TComponent);
  458. begin
  459.    inherited Create(AOwner);
  460.    ControlState := ControlState + [csCreating];
  461.    FTimerID := 0;
  462.    CreateDataBuffers(MAX_FFTLEN);
  463.    FBarDIB := TMMDIBCanvas.Create(Self);
  464.    {$IFDEF WIN32}
  465.    FpFFT := InitRealFFT(8);
  466.    {$ELSE}
  467.    FFT := TMMFFT.Create;
  468.    {$ENDIF}
  469.    FFTLen := 8;
  470.    FWindow := fwHamming;
  471.    FSampleRate := 11025;
  472.    FLogFreq := False;
  473.    FLogAmp := False;
  474.    FFreqScaleFactor := 1.0;
  475.    FFreqBase := 1.0;
  476.    Fys := 1.0;
  477.    FLogBase := 6;
  478.    FLogs := 0;
  479.    FGain3db := 0;
  480.    FDeriv := 0;
  481.    FRefFreq := 1000;
  482.    FDecay := 1;
  483.    FDecayMode := dmNone;
  484.    FDecayFactor := 0.0001;
  485.    FDecayCount := 1;
  486.    FDecayCntAct := 0;
  487.    FDecayPtr := 0;
  488.    FNumPeaks := 1;
  489.    FPeakDelay := 20;
  490.    FPeakSpeed := 0;
  491.    FDisplayPeak := False;
  492.    FKind := skBars;
  493.    FEnabled := True;
  494.    FBar1Color := clAqua;
  495.    FBar2Color := clAqua;
  496.    FBar3Color := clRed;
  497.    FInact1Color := clTeal;
  498.    FInact2Color := clTeal;
  499.    FInact3Color := clMaroon;
  500.    FScaleTextColor := clBlack;
  501.    FScaleLineColor:= clBlack;
  502.    FScaleBackColor:= clBtnFace;
  503.    FGridColor := clGray;
  504.    FPoint1 := 50;
  505.    FPoint2 := 85;
  506.    FInactiveDoted := False;
  507.    FActiveDoted := False;
  508.    FSpace := 1;
  509.    FSpotSpace := 1;
  510.    FSpotHeight := 1;
  511.    FChannel := chBoth;
  512.    FBits := b8bit;
  513.    FMode := mMono;
  514.    FGain := sgNone;
  515.    FDrawInactive := True;
  516.    FDrawFreqScale := False;
  517.    FDrawAmpScale := False;
  518.    FDrawGrid := False;
  519.    FDrawVal := nil;
  520.    FShowInfoHint := False;
  521.    FShowInfo := True;
  522.    Color := clBlack;
  523.    SetBounds(0,0,194,89);
  524.    Cursor := crCross;
  525.    ControlState := ControlState - [csCreating];
  526.    FFTLength := 128;
  527.    if not (csDesigning in ComponentState) then
  528.    begin
  529.       { create the peak timer }
  530.       FTimerID := MMTimeSetEvent(25,False,TimeCallBack,Longint(Self));
  531.    end;
  532.    ErrorCode := ComponentRegistered(InitCode, Self, ClassName);
  533.    if (ErrorCode <> 0) then RegisterFailed(InitCode, Self , ClassName);
  534. end;
  535. {-- TMMSpectrum ---------------------------------------------------------}
  536. Destructor TMMSpectrum.Destroy;
  537. begin
  538.    if (FTimerID <> 0) then
  539.    begin
  540.       { destroy the peak timer }
  541.       MMTimeKillEvent(FTimerID);
  542.    end;
  543.    FreeDataBuffers;
  544.    FreeArrays;
  545.    {$IFDEF WIN32}
  546.    DoneRealFFT(FpFFT);
  547.    {$ELSE}
  548.    FFT.Free;
  549.    {$ENDIF}
  550.    FBarDIB.Free;
  551.    inherited Destroy;
  552. end;
  553. {-- TMMSpectrum ---------------------------------------------------------}
  554. procedure TMMSpectrum.ChangeDesigning(aValue: Boolean);
  555. begin
  556.    inherited ChangeDesigning(aValue);
  557.    if not (csDesigning in ComponentState) then
  558.    begin
  559.       { create the peak timer }
  560.       if (FTimerID = 0) then
  561.           FTimerID := MMTimeSetEvent(25,False,TimeCallBack,Longint(Self));
  562.       InitializeData;
  563.    end;
  564. end;
  565. {-- TMMSpectrum ---------------------------------------------------------}
  566. procedure TMMSpectrum.SetBPP(aValue: integer);
  567. begin
  568.    if (aValue <> BitsPerPixel) then
  569.    begin
  570.       if (aValue <> 8) and (aValue <> 24) then
  571.          raise EMMDIBError.Create('Bitlength not supported yet');
  572.       FBarDIB.BitsPerPixel := aValue;
  573.       DIBCanvas.BitsPerPixel := aValue;
  574.       DrawInactiveSpots;
  575.       Invalidate;
  576.    end;
  577. //   inherited SetBPP(aValue);
  578. end;
  579. {-- TMMSpectrum ---------------------------------------------------------}
  580. procedure TMMSpectrum.GainOverflow;
  581. begin
  582.    if Assigned(FOnGainOverflow) then FOnGainOverflow(Self);
  583. end;
  584. {-- TMMSpectrum ---------------------------------------------------------}
  585. procedure TMMSpectrum.PcmOverflow;
  586. begin
  587.    if Assigned(FOnPcmOverflow) then FOnPcmOverflow(Self);
  588. end;
  589. {-- TMMSpectrum ---------------------------------------------------------}
  590. procedure TMMSpectrum.ResetDecayBuffers;
  591. var
  592.    i, j: integer;
  593. begin
  594.    FDecayPtr := 0;
  595.    FDecayCntAct := 0; { Restart the count of number of samples taken }
  596.    FillChar(FLastVal^, (FFTLen div 2)*sizeOf(Long),0);
  597.    FillChar(FLastVal_F^, (FFTLen div 2)*sizeOf(Float),0);
  598.    for i := 0 to FMaxDecayCount-1 do
  599.        for j := 0 to (FFTLen div 2)-1 do FDataBuf^[i]^[j] := 0;
  600. end;
  601. {-- TMMSpectrum ---------------------------------------------------------}
  602. procedure TMMSpectrum.ResetPeakValues;
  603. begin
  604.    FillChar(FDrawVal^[0], FWidth * sizeOf(TDrawVal), 0);
  605.    FillChar(FPeak, sizeOf(TPeak),0);
  606. end;
  607. {-- TMMSpectrum ---------------------------------------------------------}
  608. procedure TMMSpectrum.CreateDataBuffers(Length: integer);
  609. begin
  610.    if (Length > 0) then
  611.    begin
  612.       FFFTData   := GlobalAllocMem(Length * sizeOf(SmallInt));
  613.       FWinBuf    := GlobalAllocMem(Length * sizeOf(Integer));
  614.       FDisplayVal:= GlobalAllocMem((Length div 2) * sizeOf(Long));
  615.       FLastVal   := GlobalAllocMem((Length div 2) * sizeOf(Long));
  616.       FLastVal_F := GlobalAllocMem((Length div 2) * sizeOf(Float));
  617.       FYBase     := GlobalAllocMem((Length div 2) * sizeOf(Long));
  618.       FDataBuf   := GlobalAllocMem(MAXDECAYCOUNT * sizeOf(PLongArray));
  619.       {$IFDEF WIN32}
  620.       {$IFDEF TRIAL}
  621.       {$DEFINE _HACK1}
  622.       {$I MMHACK.INC}
  623.       {$ENDIF}
  624.       {$ENDIF}
  625.       FMaxDecayCount := 0;
  626.       while FMaxDecayCount < MAXDECAYCOUNT do
  627.       begin
  628.          FDataBuf^[FMaxDecayCount] := GlobalAllocMem((Length div 2) * sizeOf(Long));
  629.          if FDataBuf^[FMaxDecayCount] = nil then break;
  630.          inc(FMaxDecayCount);
  631.       end;
  632.       if (FMaxDecayCount < 1) then OutOfMemoryError;
  633.       FDecayCount := Min(FDecayCount, FMaxDecayCount);
  634.       { Clear out the memory buffers }
  635.       ResetDecayBuffers;
  636.    end;
  637. end;
  638. {-- TMMSpectrum ---------------------------------------------------------}
  639. procedure TMMSpectrum.FreeDataBuffers;
  640. var
  641.    i: integer;
  642. begin
  643.    GlobalFreeMem(Pointer(FFFTData));
  644.    GlobalFreeMem(Pointer(FWinBuf));
  645.    GlobalFreeMem(Pointer(FDisplayVal));
  646.    GlobalFreeMem(Pointer(FLastVal));
  647.    GlobalFreeMem(Pointer(FLastVal_F));
  648.    GlobalFreeMem(Pointer(FYBase));
  649.    if FDataBuf <> nil then
  650.    begin
  651.       for i := 0 to FMaxDecayCount-1 do
  652.           if FDataBuf^[i] <> nil then GlobalFreeMem(Pointer(FDataBuf^[i]));
  653.       GlobalFreeMem(Pointer(FDataBuf));
  654.    end;
  655. end;
  656. {-- TMMSpectrum ---------------------------------------------------------}
  657. procedure TMMSpectrum.CreateArrays(Size: integer);
  658. begin
  659.    if (Size > 0) then
  660.    begin
  661.       MMTimeSuspendEvent(FTimerID);
  662.       Fx1     := GlobalAllocMem((Size+10) * sizeOf(Integer));
  663.       Fx2     := GlobalAllocMem((Size+10) * sizeOf(Integer));
  664.       FYScale := GlobalAllocMem(Size * sizeOf(Integer));
  665.       FDrawVal:= GlobalAllocMem((Size+1) * sizeOf(TDrawVal));
  666.       FDrawVal^[Size].Left := -1; { mark the end }
  667.       MMTimeResumeEvent(FTimerID);
  668.    end;
  669. end;
  670. {-- TMMSpectrum ---------------------------------------------------------}
  671. procedure TMMSpectrum.FreeArrays;
  672. begin
  673.    MMTimeSuspendEvent(FTimerID);
  674.    GlobalFreeMem(Pointer(Fx1));
  675.    GlobalFreeMem(Pointer(Fx2));
  676.    GlobalFreeMem(Pointer(FYScale));
  677.    GlobalFreeMem(Pointer(FDrawVal));
  678.    MMTimeResumeEvent(FTimerID);
  679. end;
  680. {-- TMMSpectrum ---------------------------------------------------------}
  681. procedure TMMSpectrum.InitializeData;
  682. Var
  683.    i: integer;
  684. begin
  685.    FillChar(FDisplayVal^[0], FFTLen div 2 * sizeOf(Long), 0);
  686.    FillChar(FFFTData^[0], FFTLen * sizeOf(SmallInt), 0);
  687.    ResetPeakValues;
  688.    ResetDecayBuffers;
  689.    if Enabled then
  690.    begin
  691.       if assigned(FOnNeedData) then FOnNeedData(Self)
  692.       else if (csDesigning in ComponentState) then
  693.       begin
  694.          Randomize;
  695.          for i := 0 to FFTLen div 2-1 do
  696.          begin                                         { create random data }
  697.             FDisplayVal^[i] := Long(Random(32767));
  698.          end;
  699.       end;
  700.    end;
  701. end;
  702. {-- TMMSpectrum ---------------------------------------------------------}
  703. procedure TMMSpectrum.ResetData;
  704. var
  705.    P: TPoint;
  706. begin
  707.    if FShowInfoHint then
  708.    begin
  709.       GetCursorPos(P);
  710.       P := ScreenToClient(P);
  711.       Perform(WM_LBUTTONUP, 0, Longint(PointToSmallPoint(P)));
  712.    end;
  713.    InitializeData;
  714.    Refresh;
  715. end;
  716. const
  717.     inHandler: Longint = 0;
  718. {-- TMMSpectrum ---------------------------------------------------------}
  719. procedure TMMSpectrum.NeedData;
  720. begin
  721.    inc(inHandler);
  722.    try
  723.       if (inHandler = 1)
  724.       {$IFDEF BUILD_ACTIVEX}
  725.       and not Selected
  726.       {$ENDIF} then
  727.       begin
  728.          if (csLoading in ComponentState) or
  729.             (csReading in ComponentState) then exit;
  730.          InitializeData;
  731.       end;
  732.    finally
  733.       dec(inHandler);
  734.    end;
  735. end;
  736. {-- TMMSpectrum ---------------------------------------------------------}
  737. procedure TMMSpectrum.ForceRescale;
  738. begin
  739.    SetupXScale;
  740. end;
  741. {-- TMMSpectrum ---------------------------------------------------------}
  742. procedure TMMSpectrum.SetFFTLen(aLength: integer);
  743. var
  744.    Order: integer;
  745. begin
  746.    aLength := MinMax(aLength,8,MAX_FFTLEN);
  747.    { Convert FFTLen to a power of 2 }
  748.    Order := 0;
  749.    while aLength > 1 do
  750.    begin
  751.       aLength := aLength shr 1;
  752.       inc(Order);
  753.    end;
  754.    if (Order > 0) then aLength := aLength shl Order;
  755.    {$IFDEF WIN32}
  756.    {$IFDEF TRIAL}
  757.    {$DEFINE _HACK3}
  758.    {$I MMHACK.INC}
  759.    {$ENDIF}
  760.    {$ENDIF}
  761.    if (aLength <> FFTLen) then
  762.    begin
  763.       { re-init the FFT instance with the new FFT-length }
  764.       {$IFDEF WIN32}
  765.       DoneRealFFT(FpFFT);
  766.       FpFFT := InitRealFFT(Order);
  767.       FFTLen := aLength;
  768.       GenWindowTableInt(FWinBuf,Ord(FWindow),Trunc(Log2(FFTLen)));
  769.       {$ELSE}
  770.       FFT.FFTLength := aLength;
  771.       FFTLen := aLength;
  772.       GenWindowTableInt(FWinBuf,Ord(FWindow),Trunc(Log2(FFTLen)));
  773.       {$ENDIF}
  774.       { Re-initialize the display }
  775.       SetupXScale;
  776.       SetBytesPerSpectrum;
  777.       { Flush the buffers }
  778.       NeedData;
  779.       Invalidate;
  780.    end;
  781. end;
  782. {-- TMMSpectrum ---------------------------------------------------------}
  783. procedure TMMSpectrum.SetWindow(aValue: TMMFFTWindow);
  784. begin
  785.    if (aValue <> FWindow) then
  786.    begin
  787.       FWindow := aValue;
  788.       GenWindowTableInt(FWinBuf,Ord(FWindow),Trunc(Log2(FFTLen)));
  789.    end;
  790. end;
  791. {-- TMMSpectrum ---------------------------------------------------------}
  792. procedure TMMSpectrum.SetSampleRate(aValue: Longint);
  793. begin
  794.    if (aValue <> FSampleRate) then
  795.    begin
  796.       FSampleRate := MinMax(aValue, 8000,100000);
  797.       { Re-initialize the display }
  798.       SetupXScale;
  799.       NeedData;
  800.       Invalidate;
  801.    end;
  802.    {$IFDEF WIN32}
  803.    {$IFDEF TRIAL}
  804.    {$DEFINE _HACK2}
  805.    {$I MMHACK.INC}
  806.    {$ENDIF}
  807.    {$ENDIF}
  808. end;
  809. {-- TMMSpectrum ---------------------------------------------------------}
  810. procedure TMMSpectrum.SetLogFreq(aValue: Boolean);
  811. begin
  812.    { Toggle between linear and logarithmic frequency scale }
  813.    if (aValue <> FLogFreq) then
  814.    begin
  815.       FLogFreq := aValue;
  816.       SetupXScale;
  817.       NeedData;
  818.       Invalidate;
  819.    end;
  820.    {$IFDEF WIN32}
  821.    {$IFDEF TRIAL}
  822.    {$DEFINE _HACK3}
  823.    {$I MMHACK.INC}
  824.    {$ENDIF}
  825.    {$ENDIF}
  826. end;
  827. {-- TMMSpectrum ---------------------------------------------------------}
  828. procedure TMMSpectrum.SetLogAmp(aValue: Boolean);
  829. begin
  830.    { Toggle linear/logarithmic amplitude axis }
  831.    if (aValue <> FLogAmp) then
  832.    begin
  833.       FLogAmp := aValue;
  834.       if FLogAmp then SetupLogScales
  835.       else SetupLinScales;
  836.       NeedData;
  837.       Invalidate;
  838.    end;
  839.    {$IFDEF WIN32}
  840.    {$IFDEF TRIAL}
  841.    {$DEFINE _HACK1}
  842.    {$I MMHACK.INC}
  843.    {$ENDIF}
  844.    {$ENDIF}
  845. end;
  846. {-- TMMSpectrum ---------------------------------------------------------}
  847. procedure TMMSpectrum.SetDecayMode(aValue: TMMDecayMode);
  848. begin
  849.    { Select averaging mode }
  850.    if (aValue <> FDecayMode) then
  851.    begin
  852.       FDecayMode := aValue;
  853.       { Re-initialize the buffers }
  854.       ResetDecayBuffers;
  855.    end;
  856.    {$IFDEF WIN32}
  857.    {$IFDEF TRIAL}
  858.    {$DEFINE _HACK3}
  859.    {$I MMHACK.INC}
  860.    {$ENDIF}
  861.    {$ENDIF}
  862. end;
  863. {-- TMMSpectrum ---------------------------------------------------------}
  864. procedure TMMSpectrum.SetDecay(aValue: integer);
  865. var
  866.    i: integer;
  867. begin
  868.    aValue := MinMax(aValue,1,16);
  869.    if (aValue <> FDecay) then
  870.    begin
  871.       FDecay := aValue;
  872.       { factor for stepUp and exponential averaging }
  873.       FDecayFactor := 0.0001;
  874.       for i := 0 to FDecay-1 do
  875.           FDecayFactor := sqrt(FDecayFactor);
  876.       { counter for uniform averaging }
  877.       FDecayCount := MinMax(2*(aValue-1),1,MaxDecayCount);
  878.       { Re-initialize the buffers for uniform averaging }
  879.       if (FDecayMode = dmUniform) then ResetDecayBuffers;
  880.    end;
  881.    {$IFDEF WIN32}
  882.    {$IFDEF TRIAL}
  883.    {$DEFINE _HACK1}
  884.    {$I MMHACK.INC}
  885.    {$ENDIF}
  886.    {$ENDIF}
  887. end;
  888. {-- TMMSpectrum ---------------------------------------------------------}
  889. procedure TMMSpectrum.SetKind(aValue: TMMSpectrumKind);
  890. begin
  891.    if (aValue <> FKind) then
  892.    begin
  893.       FKind := aValue;
  894.       CalcNumSpots;
  895.       ResetPeakValues;
  896.       Invalidate;
  897.    end;
  898.    {$IFDEF WIN32}
  899.    {$IFDEF TRIAL}
  900.    {$DEFINE _HACK2}
  901.    {$I MMHACK.INC}
  902.    {$ENDIF}
  903.    {$ENDIF}
  904. end;
  905. {-- TMMSpectrum ---------------------------------------------------------}
  906. procedure TMMSpectrum.SetEnabled(aValue: Boolean);
  907. begin
  908.    if (aValue <> FEnabled) then
  909.    begin
  910.       FEnabled := aValue;
  911.       { inherited Enabled := Value }
  912.       if (not FEnabled) then
  913.       begin
  914.          ResetData;
  915.          MMTimeSuspendEvent(FTimerID);
  916.       end
  917.       else
  918.       begin
  919.          NeedData;                         { init Data when in designing }
  920.          MMTimeResumeEvent(FTimerID);
  921.       end;
  922.       Invalidate;
  923.    end;
  924. end;
  925. {-- TMMSpectrum ---------------------------------------------------------}
  926. procedure TMMSpectrum.Loaded;
  927. begin
  928.    inherited Loaded;
  929.    SetupXScale;
  930.    NeedData;
  931.    Invalidate;
  932.    {$IFDEF WIN32}
  933.    {$IFDEF TRIAL}
  934.    {$DEFINE _HACK3}
  935.    {$I MMHACK.INC}
  936.    {$ENDIF}
  937.    {$ENDIF}
  938. end;
  939. {-- TMMSpectrum ---------------------------------------------------------}
  940. procedure TMMSpectrum.CalcNumSpots;
  941. begin
  942.    FSpotHeight := Max(FSpotHeight, 1);
  943.    FNumSpots := (FHeight+FSpotSpace) div (FSpotHeight+FSpotSpace);
  944.    if (FNumSpots = 0) then inc(FNumSpots);     { fix divisio by zerro !!! }
  945.    FFirstSpace := (FHeight-(FNumSpots*(FSpotHeight+FSpotSpace)-FSpotSpace)) div 2;
  946.    case FKind of
  947.        skBars,
  948.        skPeaks:
  949.        begin
  950.           { calc the spot on which the next color starts }
  951.           FPoint1Spot := Round((FPoint1 * FNumSpots) / 100);
  952.           FPoint2Spot := Round((FPoint2 * FNumSpots) / 100);
  953.        end;
  954.        skLines,
  955.        skVLines:
  956.        begin
  957.           { calc the point on which the next color starts }
  958.           FPoint1Spot := Round((FPoint1 * FHeight) / 100);
  959.           FPoint2Spot := Round((FPoint2 * FHeight) / 100);
  960.        end;
  961.        skScroll:
  962.        begin
  963.           { calc the point on which the next color starts }
  964.           FPoint1Spot := Round((FPoint1 * FHeight/3) / 100);
  965.           FPoint2Spot := Round((FPoint2 * FHeight/3) / 100);
  966.        end;
  967.        else
  968.        begin
  969.           { calc the point on which the next color starts }
  970.           FPoint1Spot := Round(FHeight-((FPoint1 * FHeight) / 100));
  971.           FPoint2Spot := Round(FHeight-((FPoint2 * FHeight) / 100));
  972.        end;
  973.    end;
  974.    { prepare the second DIB with the inactive spots }
  975.    DrawInactiveSpots;
  976.    { we will see anything in designer or clear out the buffers at runtime }
  977.    NeedData;
  978. end;
  979. {-- TMMSpectrum ---------------------------------------------------------}
  980. procedure TMMSpectrum.AdjustSize(var W, H: Integer);
  981. begin
  982.    W := Max(W,2*BevelExtend+5);
  983.    H := Max(H,2*BevelExtend+5);
  984.    if FDrawAmpScale then
  985.       W := Max(W,2*SCALEWIDTH+2*BevelExtend+5);
  986.    if FDrawFreqScale then
  987.       H := Max(H,SCALEHEIGHT+2*BevelExtend+5);
  988. end;
  989. {-- TMMSpectrum ---------------------------------------------------------}
  990. procedure TMMSpectrum.AdjustBounds;
  991. var
  992.   W, H: Integer;
  993. begin
  994.    W := Width;
  995.    H := Height;
  996.    AdjustSize(W, H);
  997.    if (W <> Width) or (H <> Height) then SetBounds(Left, Top, W, H)
  998.    else Changed;
  999. end;
  1000. {-- TMMSpectrum ---------------------------------------------------------}
  1001. procedure TMMSpectrum.SetBounds(aLeft, aTop, aWidth, aHeight: integer);
  1002. var
  1003.   W, H: Integer;
  1004. begin
  1005.    W := aWidth;
  1006.    H := aHeight;
  1007.    AdjustSize (W, H);
  1008.    inherited SetBounds(aLeft, aTop, W, H);
  1009.    Changed;
  1010. end;
  1011. {-- TMMSpectrum ---------------------------------------------------------}
  1012. procedure TMMSpectrum.Changed;
  1013. begin
  1014.    FClientRect := Rect(0,0,Width,Height);
  1015.    { make place for the amp scale }
  1016.    if FDrawAmpScale then
  1017.       InflateRect(FClientRect, -SCALEWIDTH,0);
  1018.    { make place for the freq scale }
  1019.    if FDrawFreqScale then
  1020.       dec(FClientRect.Bottom, SCALEHEIGHT);
  1021.    { and now for the bevel }
  1022.    InflateRect(FClientRect, -Bevel.BevelExtend, -Bevel.BevelExtend);
  1023.    { save the real height and width }
  1024.    FWidth  := Max(FClientRect.Right - FClientRect.Left,4);
  1025.    FHeight := Max(FClientRect.Bottom - FClientRect.Top,4);
  1026.    { adjust the dyn.array size }
  1027.    FreeArrays;
  1028.    CreateArrays(FWidth);
  1029.    { set the DIB sizes }
  1030.    DIBCanvas.SetBounds(0,0,FWidth,FHeight);
  1031.    FBarDIB.SetBounds(0,0,FWidth,FHeight);
  1032.    { recalculate the number of spots }
  1033.    CalcNumSpots;
  1034.    { calc the new bytes per Scope }
  1035.    SetBytesPerSpectrum;
  1036.    { recalc the scalings }
  1037.    SetupXScale;
  1038.    { init the data buffers }
  1039.    NeedData;
  1040.    inherited Changed;
  1041. end;
  1042. {-- TMMSpectrum ---------------------------------------------------------}
  1043. procedure TMMSpectrum.SetBytesPerSpectrum;
  1044. begin
  1045.    FBytes := (Ord(FBits)+1) * (Ord(FMode)+1) * FFTLen;
  1046. end;
  1047. {-- TMMSpectrum ---------------------------------------------------------}
  1048. Procedure TMMSpectrum.SetPCMWaveFormat(wf: TPCMWaveFormat);
  1049. var
  1050.    pwfx: PWaveFormatEx;
  1051. begin
  1052.    pwfx := @wf;
  1053.    if not pcmIsValidFormat(pwfx) then
  1054.       raise EMMSpectrumError.Create(LoadResStr(IDS_INVALIDFORMAT));
  1055.    SampleRate := pwfx^.nSamplesPerSec;
  1056.    BitLength := TMMBits(pwfx^.wBitsPerSample div 8 - 1);
  1057.    Mode := TMMMode(pwfx^.nChannels-1);
  1058. end;
  1059. {-- TMMSpectrum ---------------------------------------------------------}
  1060. function TMMSpectrum.GetPCMWaveFormat: TPCMWaveFormat;
  1061. var
  1062.    wfx: TWaveFormatEx;
  1063. begin
  1064.    pcmBuildWaveHeader(@wfx, (Ord(FBits)+1)*8, Ord(FMode)+1, SampleRate);
  1065.    Result := PPCMWaveFormat(@wfx)^;
  1066. end;
  1067. {-- TMMSpectrum ---------------------------------------------------------}
  1068. Procedure TMMSpectrum.SetBits(aValue: TMMBits);
  1069. begin
  1070.    if (aValue <> FBits) then
  1071.    begin
  1072.       FBits := aValue;
  1073.       SetBytesPerSpectrum;
  1074.       Invalidate;
  1075.    end;
  1076. end;
  1077. {-- TMMSpectrum ---------------------------------------------------------}
  1078. Procedure TMMSpectrum.SetChannel(aValue: TMMChannel);
  1079. begin
  1080.    if (aValue <> FChannel) then
  1081.    begin
  1082.       FChannel := aValue;
  1083.       SetBytesPerSpectrum;
  1084.       Invalidate;
  1085.    end;
  1086.    {$IFDEF WIN32}
  1087.    {$IFDEF TRIAL}
  1088.    {$DEFINE _HACK2}
  1089.    {$I MMHACK.INC}
  1090.    {$ENDIF}
  1091.    {$ENDIF}
  1092. end;
  1093. {-- TMMSpectrum ---------------------------------------------------------}
  1094. Procedure TMMSpectrum.SetMode(aValue: TMMMode);
  1095. begin
  1096.    if (aValue <> FMode) then
  1097.    begin
  1098.       FMode := aValue;
  1099.       SetBytesPerSpectrum;
  1100.       Invalidate;
  1101.    end;
  1102. end;
  1103. {-- TMMSpectrum ---------------------------------------------------------}
  1104. procedure TMMSpectrum.SetRefFreq(aValue: integer);
  1105. begin
  1106.    aValue := MinMax(aValue,1,44100);
  1107.    if (aValue <> FRefFreq) then
  1108.    begin
  1109.       FRefFreq := aValue;
  1110.       if LogAmp then SetupLogScales
  1111.       else SetupLinScales;
  1112.       { Re-initialize the buffers }
  1113.       ResetPeakValues;
  1114.       Invalidate;
  1115.    end;
  1116.    {$IFDEF WIN32}
  1117.    {$IFDEF TRIAL}
  1118.    {$DEFINE _HACK3}
  1119.    {$I MMHACK.INC}
  1120.    {$ENDIF}
  1121.    {$ENDIF}
  1122. end;
  1123. {-- TMMSpectrum ---------------------------------------------------------}
  1124. procedure TMMSpectrum.SetGain(aValue: TMMSpectrumGain);
  1125. begin
  1126.    if (aValue <> FGain) then
  1127.    begin
  1128.       FGain := aValue;
  1129.       FDeriv := Ord(FGain) div 2;
  1130.       FGain3db := Ord(FGain) - FDeriv * 2;
  1131.       if LogAmp then SetupLogScales
  1132.       else SetupLinScales;
  1133.       ResetPeakValues;
  1134.       Invalidate;
  1135.    end;
  1136.    {$IFDEF WIN32}
  1137.    {$IFDEF TRIAL}
  1138.    {$DEFINE _HACK3}
  1139.    {$I MMHACK.INC}
  1140.    {$ENDIF}
  1141.    {$ENDIF}
  1142. end;
  1143. {-- TMMSpectrum ---------------------------------------------------------}
  1144. procedure TMMSpectrum.SetVertScale;
  1145. begin
  1146.    { Change the vertical scale factor }
  1147.    aValue := MinMax(aValue, 1, 500);
  1148.    if (aValue <> GetVertScale) then
  1149.    begin
  1150.       Fys := 0.01 * aValue;
  1151.       if LogAmp then SetupLogScales
  1152.       else SetupLinScales;
  1153. {TODO: !!!}
  1154.       (* VK_UP: Increase the vertical scale factor }
  1155.       if LogAmp then
  1156.       begin
  1157.          if (Log_Base < 10) then
  1158.          begin
  1159.             inc(Logs, 1);
  1160.             inc(Log_Base, 1);
  1161.             if (Log_Base > 10) then
  1162.             begin
  1163.                Logs := Logs - (Log_Base-10);
  1164.                Log_Base := 10;
  1165.             end;
  1166.             Setup_LogScales;
  1167.          end;
  1168.       end;
  1169.       VK_DOWN: { Decrease the vertical scale factor }
  1170.       if LogAmp then
  1171.       begin
  1172.          if (Logs > 0) then
  1173.          begin
  1174.             dec(Logs, 1);
  1175.             dec(Log_Base, 1);
  1176.             if (Logs < 0) then
  1177.             begin
  1178.                dec(Log_Base, Logs);
  1179.                Logs := 0;
  1180.             end;
  1181.             Setup_LogScales;
  1182.          end;
  1183.       end*)
  1184.       ResetPeakValues;
  1185.       Invalidate;
  1186.    end;
  1187. end;
  1188. {-- TMMSpectrum ---------------------------------------------------------}
  1189. function TMMSpectrum.GetVertScale: integer;
  1190. begin
  1191.    Result := Round(Fys / 0.01);
  1192. end;
  1193. {-- TMMSpectrum ---------------------------------------------------------}
  1194. procedure TMMSpectrum.SetFreqScale(aValue: integer);
  1195. var
  1196.    i: integer;
  1197. begin
  1198.    aValue := MinMax(aValue,1,16);
  1199.    { Convert scale to a power of 2 }
  1200.    i := 0;
  1201.    while aValue > 1 do
  1202.    begin
  1203.       aValue := aValue shr 1;
  1204.       inc(i);
  1205.    end;
  1206.    if (i > 0) then aValue := aValue shl i;
  1207.    if (aValue <> Trunc(FFreqScaleFactor)) then
  1208.    begin
  1209.       FFreqScaleFactor := aValue;
  1210.       { Re-initialize the display }
  1211.       SetupXScale;
  1212.       NeedData;
  1213.       Invalidate;
  1214.    end;
  1215.    {$IFDEF WIN32}
  1216.    {$IFDEF TRIAL}
  1217.    {$DEFINE _HACK1}
  1218.    {$I MMHACK.INC}
  1219.    {$ENDIF}
  1220.    {$ENDIF}
  1221. end;
  1222. {-- TMMSpectrum ---------------------------------------------------------}
  1223. function TMMSpectrum.GetFreqScale: integer;
  1224. begin
  1225.    Result := Trunc(FFreqScaleFactor);
  1226. end;
  1227. {-- TMMSpectrum ---------------------------------------------------------}
  1228. procedure TMMSpectrum.SetDrawFreqScale(aValue: Boolean);
  1229. begin
  1230.    if (aValue <> FDrawFreqScale) then
  1231.    begin
  1232.       FDrawFreqScale := aValue;
  1233.       AdjustBounds;
  1234.       Refresh;
  1235.    end;
  1236.    {$IFDEF WIN32}
  1237.    {$IFDEF TRIAL}
  1238.    {$DEFINE _HACK2}
  1239.    {$I MMHACK.INC}
  1240.    {$ENDIF}
  1241.    {$ENDIF}
  1242. end;
  1243. {-- TMMSpectrum ---------------------------------------------------------}
  1244. procedure TMMSpectrum.SetDrawAmpScale(aValue: Boolean);
  1245. begin
  1246.    if (aValue <> FDrawAmpScale) then
  1247.    begin
  1248.       FDrawAmpScale := aValue;
  1249.       AdjustBounds;
  1250.       Refresh;
  1251.    end;
  1252.    {$IFDEF WIN32}
  1253.    {$IFDEF TRIAL}
  1254.    {$DEFINE _HACK3}
  1255.    {$I MMHACK.INC}
  1256.    {$ENDIF}
  1257.    {$ENDIF}
  1258. end;
  1259. {-- TMMSpectrum ---------------------------------------------------------}
  1260. procedure TMMSpectrum.SetDrawGrid(aValue: Boolean);
  1261. begin
  1262.    if (aValue <> FDrawGrid) then
  1263.    begin
  1264.       FDrawGrid := aValue;
  1265.       Invalidate;
  1266.    end;
  1267.    {$IFDEF WIN32}
  1268.    {$IFDEF TRIAL}
  1269.    {$DEFINE _HACK1}
  1270.    {$I MMHACK.INC}
  1271.    {$ENDIF}
  1272.    {$ENDIF}
  1273. end;
  1274. {-- TMMSpectrum ---------------------------------------------------------}
  1275. procedure TMMSpectrum.CMColorChanged(var Message: TMessage);
  1276. begin
  1277.    DrawInactiveSpots;
  1278.    inherited;
  1279. end;
  1280. {-- TMMSpectrum ---------------------------------------------------------}
  1281. Procedure TMMSpectrum.SetColors(Index: Integer; Value: TColor);
  1282. begin
  1283.    case Index of
  1284.         0: if FBar1Color = Value then exit else FBar1Color := Value;
  1285.         1: if FBar2Color = Value then exit else FBar2Color := Value;
  1286.         2: if FBar3Color = Value then exit else FBar3Color := Value;
  1287.         3: if FInact1Color = Value then exit else FInact1Color := Value;
  1288.         4: if FInact2Color = Value then exit else FInact2Color := Value;
  1289.         5: if FInact3Color = Value then exit else FInact3Color := Value;
  1290.         6: if FScaleTextColor = Value then exit else FScaleTextColor := Value;
  1291.         7: if FScaleLineColor = Value then exit else FScaleLineColor := Value;
  1292.         8: if FGridColor = Value then exit else FGridColor := Value;
  1293.         9: if FScaleBackColor = Value then exit else FScaleBackColor := Value;
  1294.    end;
  1295.    DrawInactiveSpots;
  1296.    Invalidate;
  1297. end;
  1298. {-- TMMSpectrum ---------------------------------------------------------}
  1299. Procedure TMMSpectrum.SetPoints(Index, aValue: integer);
  1300. begin
  1301.    if (aValue>=1) and (aValue<=100) then
  1302.    begin
  1303.       case Index of
  1304.          0: if FPoint1 = aValue then exit else FPoint1 := aValue;
  1305.          1: if FPoint2 = aValue then exit else FPoint2 := aValue;
  1306.       end;
  1307.       CalcNumSpots;
  1308.       Invalidate;
  1309.    end;
  1310. end;
  1311. {-- TMMSpectrum ---------------------------------------------------------}
  1312. Procedure TMMSpectrum.SetSpace(aValue: integer);
  1313. begin
  1314.    if (aValue <> FSpace) AND (aValue >= 0) AND (aValue <= 5) then
  1315.    begin
  1316.       FSpace := aValue;
  1317.       ResetPeakValues;
  1318.       DrawInactiveSpots;
  1319.       Invalidate;
  1320.    end;
  1321. end;
  1322. {-- TMMSpectrum ---------------------------------------------------------}
  1323. Procedure TMMSpectrum.SetSpotSpace(aValue: integer);
  1324. begin
  1325.    if (aValue <> FSpotSpace) AND (aValue >= 0) AND (aValue <= 10) then
  1326.    begin
  1327.       FSpotSpace := aValue;
  1328.       CalcNumSpots;
  1329.       ResetPeakValues;
  1330.       Invalidate;
  1331.    end;
  1332.    {$IFDEF WIN32}
  1333.    {$IFDEF TRIAL}
  1334.    {$DEFINE _HACK1}
  1335.    {$I MMHACK.INC}
  1336.    {$ENDIF}
  1337.    {$ENDIF}
  1338. end;
  1339. {-- TMMSpectrum ---------------------------------------------------------}
  1340. Procedure TMMSpectrum.SetSpotHeight(aValue: integer);
  1341. begin
  1342.    if (aValue <> FSpotHeight) and (aValue > 0) and (aValue <= FHeight div 3) then
  1343.    begin
  1344.       FSpotHeight := aValue;
  1345.       CalcNumSpots;
  1346.       ResetPeakValues;
  1347.       Invalidate;
  1348.    end;
  1349.    {$IFDEF WIN32}
  1350.    {$IFDEF TRIAL}
  1351.    {$DEFINE _HACK2}
  1352.    {$I MMHACK.INC}
  1353.    {$ENDIF}
  1354.    {$ENDIF}
  1355. end;
  1356. {-- TMMSpectrum ---------------------------------------------------------}
  1357. procedure TMMSpectrum.SetDrawInactive(aValue: Boolean);
  1358. begin
  1359.    if (aValue <> FDrawInactive) then
  1360.    begin
  1361.       FDrawInactive := aValue;
  1362.       Invalidate;
  1363.    end;
  1364. end;
  1365. {-- TMMSpectrum ---------------------------------------------------------}
  1366. procedure TMMSpectrum.SetInactiveDoted(aValue: Boolean);
  1367. begin
  1368.    if (aValue <> FInactiveDoted) then
  1369.    begin
  1370.       FInactiveDoted := aValue;
  1371.       DrawInactiveSpots;
  1372.       Invalidate;
  1373.    end;
  1374.    {$IFDEF WIN32}
  1375.    {$IFDEF TRIAL}
  1376.    {$DEFINE _HACK1}
  1377.    {$I MMHACK.INC}
  1378.    {$ENDIF}
  1379.    {$ENDIF}
  1380. end;
  1381. {-- TMMSpectrum ---------------------------------------------------------}
  1382. procedure TMMSpectrum.SetActiveDoted(aValue: Boolean);
  1383. begin
  1384.    if (aValue <> FActiveDoted) then
  1385.    begin
  1386.       FActiveDoted := aValue;
  1387.       Invalidate;
  1388.    end;
  1389.    {$IFDEF WIN32}
  1390.    {$IFDEF TRIAL}
  1391.    {$DEFINE _HACK2}
  1392.    {$I MMHACK.INC}
  1393.    {$ENDIF}
  1394.    {$ENDIF}
  1395. end;
  1396. {-- TMMSpectrum ---------------------------------------------------------}
  1397. procedure TMMSpectrum.SetPeakDelay(aValue: integer);
  1398. begin
  1399.    if (aValue <> FPeakDelay) AND (aValue >= 0) AND (aValue <= 50) then
  1400.    begin
  1401.       FPeakDelay := aValue;
  1402.       ResetPeakValues;
  1403.       Invalidate;
  1404.    end;
  1405.    {$IFDEF WIN32}
  1406.    {$IFDEF TRIAL}
  1407.    {$DEFINE _HACK3}
  1408.    {$I MMHACK.INC}
  1409.    {$ENDIF}
  1410.    {$ENDIF}
  1411. end;
  1412. {-- TMMSpectrum ---------------------------------------------------------}
  1413. procedure TMMSpectrum.SetPeakSpeed(aValue: integer);
  1414. begin
  1415.    if (aValue <> FPeakSpeed) AND (aValue >= 0) AND (aValue <= 50) then
  1416.    begin
  1417.       FPeakSpeed := aValue;
  1418.       ResetPeakValues;
  1419.       Invalidate;
  1420.    end;
  1421.    {$IFDEF WIN32}
  1422.    {$IFDEF TRIAL}
  1423.    {$DEFINE _HACK1}
  1424.    {$I MMHACK.INC}
  1425.    {$ENDIF}
  1426.    {$ENDIF}
  1427. end;
  1428. {-- TMMSpectrum ---------------------------------------------------------}
  1429. procedure TMMSpectrum.SetNumPeaks(aValue: integer);
  1430. begin
  1431.    if (aValue <> FNumPeaks) AND (aValue >= 0) AND (aValue <= 5) then
  1432.    begin
  1433.       FNumPeaks := aValue;
  1434.       ResetPeakValues;
  1435.       if (FNumPeaks = 0) then
  1436.          MMTimeSuspendEvent(FTimerID)
  1437.       else if FEnabled then
  1438.          MMTimeResumeEvent(FTimerID);
  1439.       Invalidate;
  1440.    end;
  1441.    {$IFDEF WIN32}
  1442.    {$IFDEF TRIAL}
  1443.    {$DEFINE _HACK2}
  1444.    {$I MMHACK.INC}
  1445.    {$ENDIF}
  1446.    {$ENDIF}
  1447. end;
  1448. {-- TMMSpectrum ---------------------------------------------------------}
  1449. procedure TMMSpectrum.SetDisplayPeak(aValue: Boolean);
  1450. begin
  1451.    if (aValue <> FDisplayPeak) then
  1452.    begin
  1453.       FDisplayPeak := aValue;
  1454.       Invalidate;
  1455.    end;
  1456. end;
  1457. {-- TMMSpectrum ---------------------------------------------------------}
  1458. function TMMSpectrum.GetPeak: TPeak;
  1459. var
  1460.    re,im: Float;
  1461. begin
  1462.    with FPeak do
  1463.    begin
  1464.       if (FDecayMode <> dmNone) then
  1465.       begin
  1466.          re := FDisplayVal^[index]/16.0;
  1467.          im := 0;
  1468.       end
  1469.       else
  1470.       begin
  1471.          {$IFDEF WIN32}
  1472.          re := FFFTData^[2*index];
  1473.          im := FFFTData^[2*index+1];
  1474.          {$ELSE}
  1475.          re := FFFTData^[FFT.BitReversed^[index]];
  1476.          im := FFFTData^[FFT.BitReversed^[index]+1];
  1477.          {$ENDIF}
  1478.       end;
  1479.       amp := sqrt(re*re+im*im)/32768.0;
  1480.       if (FGain3db > 0) then
  1481.          amp := amp * sqrt((index+1)*FSampleRate/FFTLen/FRefFreq);
  1482.       if (FDeriv = 1) then
  1483.          amp := amp * FSampleRate/(2*M_PI*FRefFreq);
  1484.       if (FDeriv = 2) then
  1485.          amp := amp * FSampleRate/(2*M_PI*FRefFreq)
  1486.             * FSampleRate/(2*M_PI*FRefFreq);
  1487.       if (amp <> 0) and (FPeak.Amplitude > 0) then
  1488.       begin
  1489.          db := 20*log10(amp);
  1490.          if FLogFreq then
  1491.          begin
  1492.             if index <= 1 then Freq := (index+0.25) * FSampleRate/FFTLen
  1493.             else Freq := index * FSampleRate/FFTLen;
  1494.          end
  1495.          else Freq := (index+0.5) * FSampleRate/FFTLen;
  1496.       end
  1497.       else
  1498.       begin
  1499.          amp := 0;
  1500.          db := -100;
  1501.          Freq := 0;
  1502.       end;
  1503.    end;
  1504.    Result := FPeak;
  1505. end;
  1506. {-- TMMSpectrum ---------------------------------------------------------}
  1507. { Set up logarithmic amplitude (Y) scale factors and offsets. }
  1508. procedure TMMSpectrum.SetupLogScales;
  1509. var
  1510.    i: integer;
  1511.    Scale,Base,Convert,Offset: Float;
  1512. begin
  1513.    if not(csLoading in ComponentState) then
  1514.    begin
  1515.       { Compute the (logarithmic) y scale factor and offset.
  1516.         This may include a 3dB/octave gain.
  1517.         Conversion factor from db/10 to dPhils (the computed "unit")
  1518.         where a factor of 2 yields 16384 dPhils (6.02dB)
  1519.         Scaling factor is such that  32768 ->   0.00 dB -> 245760 dPhils
  1520.                                 and      2 -> -84.29 dB ->  16384 dPhils
  1521.                                 and      1 -> -90.31 dB ->      0 dPhils
  1522.         i.e. dPhils=16384.0/log(2) * log(value)
  1523.         and changes of 6.02 dB = 16384 dPhils }
  1524.       Convert := 819.2*log(10)/log(2); { Scale for dB to dPhils conversion  }
  1525.       Offset := log10(32768)*20;       { Offset for db to dPhils conversion }
  1526.       { This value is used in the main program group to convert squared values
  1527.         amplitudes to dPhils using dPhils = log(value^2)*Log_ScaleFactor }
  1528.       FLogScaleFactor := 8192.0/log(2);
  1529.       Scale := FHeight/(10*(FLogBase-FLogs)*Convert);
  1530.       if (FDeriv = 0) then
  1531.          Base := (Offset-FLogBase*10)*Convert
  1532.       else if(FDeriv = 1) then
  1533.          Base := (Offset-log10(FSampleRate/(2*M_PI*FRefFreq))*20-FLogBase*10)*Convert
  1534.       else
  1535.          Base := (Offset-log10(FSampleRate/(2*M_PI*FRefFreq))*40-FLogBase*10)*Convert;
  1536.       FDispScaleFactor := Scale; { Save the unshifted version for avg. display mode }
  1537.       FShift := 0;
  1538.       {  Make maximum use of available bits
  1539.          (use only 12 bits--other 4 used for higher resolution in the data) }
  1540.       while (Scale < 4096) do
  1541.       begin
  1542.          Scale := Scale*2;
  1543.          inc(FShift);
  1544.       end;
  1545.       for i := 0 to FWidth-1 do
  1546.           FYScale^[i] := Floor(Scale+0.5);
  1547.       if (FGain3db > 0) then
  1548.       begin
  1549.          for i := 0 to (FFTLen div 2)-1 do
  1550.      FYBase^[i] := Floor(0.5+Base-log10((i+1)*FSampleRate/FFTLen/FRefFreq)*Convert*10);
  1551.       end
  1552.       else
  1553.       begin
  1554.          for i := 0 to (FFTLen div 2)-1 do
  1555.      FYBase^[i] := Floor(0.5+Base);
  1556.       end;
  1557.    end;
  1558. end;
  1559. {-- TMMSpectrum ---------------------------------------------------------}
  1560. { Set up linear amplitude (Y) scale factors }
  1561. procedure TMMSpectrum.SetupLinScales;
  1562. var
  1563.    i: integer;
  1564.    Scale: Float;
  1565. begin
  1566.    if not(csLoading in ComponentState) then
  1567.    begin
  1568.       { Compute the (linear) y scale factor.
  1569.         This may include  a 3dB/octave gain. }
  1570.       Scale := FHeight/(Fys*32768.0*sqrt(FRefFreq));
  1571.       FShift := 4; { Display data has an extra factor of 16 for better resolution }
  1572.       if (FDeriv = 1) then
  1573.       begin
  1574.          Scale := Scale*FSampleRate/(2*M_PI*FRefFreq);
  1575.       end
  1576.       else if (FDeriv = 2) then
  1577.       begin
  1578.          Scale := Scale*FSampleRate*FSampleRate/(4*M_PI*M_PI*FRefFreq*FRefFreq);
  1579.       end;
  1580.       { Make maximum use of available bits }
  1581.       if (FGain3db > 0) then
  1582.       begin
  1583.          { Make maximum use of available bits
  1584.          (use only 12 bits--other 4 used for higher resolution in the data) }
  1585.          while Scale*sqrt(FSampleRate/2) < 4096 do
  1586.          begin
  1587.     Scale := Scale*2;
  1588.     inc(FShift);
  1589.          end;
  1590.          for i := 0 to FWidth-1 do
  1591.          begin
  1592.     if (Fx1^[i] = -1) then FYScale^[i] := 0
  1593.     else FYScale^[i] := Round(Scale*sqrt((Fx1^[i]+1)*FSampleRate/FFTLen)+0.5);
  1594.          end;
  1595.       end
  1596.       else
  1597.       begin
  1598.          { Make maximum use of available bits
  1599.          (use only 12 bits--other 4 used for higher resolution in the data) }
  1600.          Scale := Scale*sqrt(FRefFreq);
  1601.          while (Scale < 4096) do
  1602.          begin
  1603.     Scale := Scale*2;
  1604.     inc(FShift);
  1605.          end;
  1606.          for i := 0 to FWidth-1 do
  1607.          begin
  1608.     if (Fx1^[i] = -1) then FYScale^[i] := 0
  1609.     else FYScale^[i] := Floor(Scale+0.5);
  1610.          end;
  1611.       end;
  1612.    end;
  1613. end;
  1614. {-- TMMSpectrum ---------------------------------------------------------}
  1615. procedure TMMSpectrum.XRangeCheck;
  1616. var
  1617.    MaxBase: Float;
  1618. begin
  1619.    FFreqScaleFactor := MinMaxR(FFreqScaleFactor, 1.0, 16.0);
  1620.    if FLogFreq then
  1621.    begin
  1622.       MaxBase := FSampleRate/2/exp(log(FFTLen/2)/FFreqScaleFactor);
  1623.       FFreqBase := MinMaxR(FFreqBase, FSampleRate/FFTLen, MaxBase);
  1624.    end
  1625.    else
  1626.    begin
  1627.       FFreqBase := MaxR(FFreqBase, 0);
  1628.       if ((FFreqBase+FSampleRate/(2*FFreqScaleFactor))>FSampleRate/2) then
  1629.  FFreqBase := FSampleRate/2-FSampleRate/(2*FFreqScaleFactor);
  1630.    end;
  1631. end;
  1632. {-- TMMSpectrum ---------------------------------------------------------}
  1633. { Set up X axis scales }
  1634. procedure TMMSpectrum.SetupXScale;
  1635. var
  1636.    i,ival: Long;
  1637. begin
  1638.    if not(csLoading in ComponentState) then
  1639.    begin
  1640.       { Do some range checking on the base and scale factors }
  1641.       XRangeCheck;
  1642.       if assigned(FOnGetXScale) then FOnGetXScale(Self,Fx1,Fx2)
  1643.       else
  1644.       begin
  1645.          { Initialize graph x scale (linear or logarithmic).
  1646.            This array points to the bin to be plotted on a given line.}
  1647.          for i := 0 to FWidth-1 do
  1648.          begin
  1649.             if FLogFreq then
  1650.                ival := Floor(FFTLen*FFreqBase/FSampleRate*exp((i-0.45)/
  1651.                              FWidth*Log((FFTLen+1)/2)/FFreqScaleFactor)+0.51)-1
  1652.             else
  1653.                ival := Floor((i/FWidth*FFTLen/2.0/FFreqScaleFactor)+
  1654.                              (FFreqBase/FSampleRate*FFTLen)+0.01);
  1655.             ival := MinMax(ival,0,(FFTLen div 2)-1);
  1656.             Fx1^[i] := ival;
  1657.             if (i > 0) then Fx2^[i-1] := ival;
  1658.          end;
  1659.          { Compute the ending locations for lines holding multiple bins }
  1660.          for i := 0 to FWidth-1 do
  1661.              if (Fx2^[i] <= (Fx1^[i]+1)) then Fx2^[i] := 0;
  1662.       end;
  1663.       { If lines are repeated on the screen, flag this so that we don't
  1664.         have to recompute the y values. }
  1665.       for i := FWidth-1 downTo 1 do
  1666.       begin
  1667.          if (Fx1^[i] = Fx1^[i-1]) then
  1668.          begin
  1669.             Fx1^[i] := -1;
  1670.             Fx2^[i]:= 0;
  1671.          end;
  1672.       end;
  1673.       if FLogAmp then SetupLogScales
  1674.       else SetupLinScales;
  1675.       DrawInactiveSpots;
  1676.       if not (csDesigning in ComponentState) then
  1677.          FastDraw(DrawFrequencyScale,True)
  1678.       else
  1679.          Invalidate;
  1680.    end;
  1681. end;
  1682. {-- TMMSpectrum ---------------------------------------------------------}
  1683. function TMMSpectrum.GetFrequencyAtPos(Pos: TPoint): Float;
  1684. var
  1685.    Step: Float;
  1686. begin
  1687.    Result := 0;
  1688.    if PtInRect(FClientRect,Pos) then
  1689.    begin
  1690.       dec(Pos.X,FClientRect.Left);
  1691.       if (FLogFreq) then
  1692.       begin
  1693.          Step := log(FFTLen/2)/((FWidth-1)*FFreqScaleFactor);
  1694.          Result := MaxR(FFreqBase*exp(Pos.X*Step),0);
  1695.       end
  1696.       else
  1697.       begin
  1698.          Step := (FSampleRate/2-FFreqBase)/(FWidth-1)/FFreqScaleFactor;
  1699.          Result := MaxR(FFreqBase+Pos.X*Step,0);
  1700.       end;
  1701.    end;
  1702. end;
  1703. {-- TMMSpectrum ---------------------------------------------------------}
  1704. function TMMSpectrum.GetAmplitudeAtPos(Pos: TPoint): Float;
  1705. begin
  1706.    Result := 0;
  1707.    if PtInRect(FClientRect,Pos) then
  1708.    begin
  1709.       dec(Pos.Y,FClientRect.Top);
  1710.       if FLogAmp then
  1711.          Result := (Pos.Y*((FLogBase-FLogs)/(FHeight-1))+FLogs)*-10
  1712.       else
  1713.          Result := (FHeight-Pos.Y-1)*(10/(FHeight-1))*Fys*0.1;
  1714.    end;
  1715. end;
  1716. {-- TMMSpectrum ---------------------------------------------------------}
  1717. function TMMSpectrum.GetScaleBackColor: TColor;
  1718. begin
  1719.    {$IFNDEF BUILD_ACTIVEX}
  1720.    Result := TForm(Parent).Color;
  1721.    {$ELSE}
  1722.    Result := FScaleBackColor;
  1723.    {$ENDIF}
  1724. end;
  1725. {-- TMMSpectrum ---------------------------------------------------------}
  1726. procedure TMMSpectrum.DrawFrequencyScale(Dummy: Boolean);
  1727. var
  1728.    aBitmap: TBitmap;
  1729.    i, X: integer;
  1730.    Step, Freq: Float;
  1731.    Text: String;
  1732.    NumSteps: integer;
  1733. begin
  1734.    if FDrawFreqScale then
  1735.    begin
  1736.       aBitmap := TBitmap.Create;
  1737.       try
  1738.          aBitmap.Width := FWidth + 2*BevelExtend;
  1739.          aBitmap.Height := SCALEHEIGHT;
  1740.          aBitmap.Canvas.Font.Color := FScaleTextColor;
  1741.          aBitmap.Canvas.Pen.Color := FScaleLineColor;
  1742.          aBitmap.Canvas.Brush.Color := GetScaleBackColor;
  1743.          with aBitmap.Canvas do
  1744.          begin
  1745.             FillRect(Rect(0,0,aBitmap.Width,aBitmap.Height));
  1746.             { calc the number of steps required }
  1747.             NumSteps := 32;
  1748.             while (FWidth div NumSteps < SCALEFONTSIZE) do
  1749.             begin
  1750.                NumSteps := NumSteps div 2;
  1751.                if NumSteps = 1 then break;
  1752.             end;
  1753.             { Put up the frequency scale. }
  1754.             if (FLogFreq) then
  1755.                 Step := log(FFTLen/2)/(NumSteps*FFreqScaleFactor)
  1756.             else
  1757.                 Step := (FSampleRate/2-FFreqBase)/NumSteps/FFreqScaleFactor;
  1758.             MoveTo(BevelExtend,0);
  1759.             for i := 0 to NumSteps do
  1760.             begin
  1761.                X := BevelExtend + Round(i * (FWidth-1)/NumSteps);
  1762.                LineTo(X, 0);
  1763.                LineTo(X, 3);
  1764.                MoveTo(X, 0);
  1765.                if (FLogFreq) then
  1766.                    Freq := MaxR(FFreqBase*exp(Step*i),0)
  1767.                else
  1768.                    Freq := MaxR(FFreqBase+i*step,0);
  1769.                Text := Format('%.0f',[Freq]);
  1770.                TextOutAligned(aBitmap.Canvas,X,6,Text,SCALEFONT,SCALEFONTSIZE,2);{ vertical text }
  1771.             end;
  1772.          end;
  1773.          Canvas.Draw(FClientRect.Left-BevelExtend,
  1774.                      FClientRect.Bottom+BevelExtend+3, aBitmap);
  1775.       finally
  1776.          aBitmap.Free;
  1777.       end;
  1778.    end;
  1779. end;
  1780. {-- TMMSpectrum ---------------------------------------------------------}
  1781. procedure TMMSpectrum.DrawAmplitudeScale;
  1782. var
  1783.    aBitmap: TBitmap;
  1784.    i, X, Y, H: integer;
  1785.    Text: String;
  1786.    Scale: Float;
  1787.    NumSteps: integer;
  1788. begin
  1789.    { Put up the amplitude scale }
  1790.    if FDrawAmpScale then
  1791.    begin
  1792.       aBitmap := TBitmap.Create;
  1793.       try
  1794.          if FdrawFreqScale then
  1795.             H := Height-ScaleHeight
  1796.          else
  1797.             H := Height;
  1798.          aBitmap.Width := SCALEWIDTH;
  1799.          aBitmap.Height := H;
  1800.          aBitmap.Canvas.Font.Color := FScaleTextColor;
  1801.          aBitmap.Canvas.Pen.Color := FScaleLineColor;
  1802.          aBitmap.Canvas.Brush.Color := GetScaleBackColor;
  1803.          with aBitmap.Canvas do
  1804.          begin
  1805.             if (LogAmp) then
  1806.             begin
  1807.                { calc the number of steps required }
  1808.                NumSteps := (FLogBase-FLogs);
  1809.                while (FHeight div NumSteps < SCALEFONTSIZE) do
  1810.                begin
  1811.                   dec(NumSteps);
  1812.                   if NumSteps <= 1 then break;
  1813.                end;
  1814.                { draw the left side }
  1815.                FillRect(Rect(0,0,aBitmap.Width,aBitmap.Height));
  1816.                X := SCALEWIDTH-1;
  1817.                MoveTo(X, BevelExtend);
  1818.                for i := 0 to NumSteps do
  1819.                begin
  1820.                   Y := BevelExtend + Trunc(i*(FHeight-1)/NumSteps);
  1821.                   LineTo(X, Y);
  1822.                   LineTo(X-3, Y);
  1823.                   MoveTo(X, Y);
  1824.           Text := Format('%d',[Round((i*((FLogBase-FLogs)/NumSteps)+FLogs)*-10)]);
  1825.                   TextOutAligned(aBitmap.Canvas, X-4, Y, Text, SCALEFONT,SCALEFONTSIZE, 1);{ right text }
  1826.                end;
  1827.                Canvas.Draw(-3, 0, aBitmap);
  1828.                { draw the right side }
  1829.                FillRect(Rect(0,0,aBitmap.Width,aBitmap.Height));
  1830.                X := 0;
  1831.                MoveTo(X, BevelExtend);
  1832.                for i := 0 to NumSteps do
  1833.                begin
  1834.                   Y := BevelExtend + Trunc(i*(FHeight-1)/NumSteps);
  1835.                   LineTo(X, Y);
  1836.                   LineTo(X+3, Y);
  1837.                   MoveTo(X, Y);
  1838.                   Text := Format('%2.d',[Round((i*((FLogBase-FLogs)/NumSteps)+FLogs)*-10)]);
  1839.                   TextOutAligned(aBitmap.Canvas, X+6, Y, Text, SCALEFONT,SCALEFONTSIZE,0);
  1840.                end;
  1841.                Canvas.Draw((Width - SCALEWIDTH)+3, 0, aBitmap);
  1842.             end
  1843.             else
  1844.             begin
  1845.                { calc the number of steps required }
  1846.                NumSteps := 10;
  1847.                while (FHeight div NumSteps < SCALEFONTSIZE) do
  1848.                begin
  1849.                   dec(NumSteps);
  1850.                   if NumSteps <= 1 then break;
  1851.                end;
  1852.                { calc the scaling steps }
  1853.                Scale := (Fys*32768.0)/FHeight;
  1854.                { draw the left side }
  1855.                FillRect(Rect(0,0,aBitmap.Width,aBitmap.Height));
  1856.                X := SCALEWIDTH-1;
  1857.                MoveTo(X, H-BevelExtend-1);
  1858.                for i := 0 to NumSteps do
  1859.                begin
  1860.                   if (Fys > 0.095) then Text := Format('%4.2f',[i*(10/NumSteps)*Fys*0.1])
  1861.           else Text := Format('%5.3f',[i*(10/NumSteps)*Fys*0.1]);
  1862.                   Y := H-BevelExtend-Trunc(i*Fys*32760.0/NumSteps/Scale)-1;
  1863.                   LineTo(X, Y);
  1864.                   LineTo(X-3, Y);
  1865.                   MoveTo(X, Y);
  1866.           TextOutAligned(aBitmap.Canvas, X-5, Y, Text,SCALEFONT,SCALEFONTSIZE,1);{ right text }
  1867.                end;
  1868.                Canvas.Draw(-3, 0, aBitmap);
  1869.                { draw the right side }
  1870.                FillRect(Rect(0,0,aBitmap.Width,aBitmap.Height));
  1871.                X := 0;
  1872.                MoveTo(X, H-BevelExtend-1);
  1873.                for i := 0 to NumSteps do
  1874.                begin
  1875.                   if (Fys > 0.095) then Text := Format('%4.2f',[i*(10/NumSteps)*Fys*0.1])
  1876.           else Text := Format('%5.3f',[i*(10/NumSteps)*Fys*0.1]);
  1877.                   Y := H-BevelExtend-Trunc(i*Fys*32760.0/NumSteps/Scale)-1;
  1878.                   LineTo(X, Y);
  1879.                   LineTo(X+3, Y);
  1880.                   MoveTo(X, Y);
  1881.           TextOutAligned(aBitmap.Canvas, X+6, Y, Text, SCALEFONT,SCALEFONTSIZE,0);{ left text }
  1882.                end;
  1883.                Canvas.Draw((Width - SCALEWIDTH)+3, 0, aBitmap);
  1884.             end;
  1885.          end;
  1886.       finally
  1887.          aBitmap.Free;
  1888.       end;
  1889.    end;
  1890. end;
  1891. {-- TMMSpectrum ---------------------------------------------------------}
  1892. procedure TMMSpectrum.DrawGrids;
  1893. var
  1894.    i,X,Y,NumSteps: integer;
  1895.    Scale: Float;
  1896. begin
  1897.    if FDrawGrid then
  1898.    with DIBCanvas do
  1899.    begin
  1900.       DIB_SetTColor(FGridColor);
  1901.       { the horizontal lines }
  1902.       if (LogAmp) then
  1903.       begin
  1904.          { calc the number of steps required }
  1905.          NumSteps := (FLogBase-FLogs);
  1906.          while (FHeight div NumSteps < SCALEFONTSIZE) do
  1907.          begin
  1908.             dec(NumSteps);
  1909.             if NumSteps <= 1 then break;
  1910.          end;
  1911.          for i := 0 to NumSteps do
  1912.          begin
  1913.             Y := Trunc(i*(FHeight-1)/NumSteps);
  1914.             DIB_HLineDoted(0, FWidth, Y, 1);
  1915.          end;
  1916.       end
  1917.       else
  1918.       begin
  1919.          { calc the number of steps required }
  1920.          NumSteps := 10;
  1921.          while (FHeight div NumSteps < SCALEFONTSIZE) do
  1922.          begin
  1923.             dec(NumSteps);
  1924.             if NumSteps <= 1 then break;
  1925.          end;
  1926.          { calc the scale steps required }
  1927.          Scale := (Fys*32768.0)/FHeight;
  1928.          for i := 0 to NumSteps do
  1929.          begin
  1930.             Y := FHeight-Trunc(i*Fys*32760.0/NumSteps/Scale)-1;
  1931.             DIB_HLineDoted(0, FWidth, Y, 1);
  1932.          end;
  1933.       end;
  1934.       { the vertical lines }
  1935.       { calc the number of steps required }
  1936.       NumSteps := 32;
  1937.       while (FWidth div NumSteps < SCALEFONTSIZE) do
  1938.       begin
  1939.          NumSteps := NumSteps div 2;
  1940.          if NumSteps = 1 then break;
  1941.       end;
  1942.       for i := 0 to NumSteps do
  1943.       begin
  1944.          X := i * (FWidth-1) div NumSteps;
  1945.          DIB_VLineDoted(X, 0, FHeight, 1);
  1946.       end;
  1947.    end;
  1948. end;
  1949. {-- TMMSpectrum ---------------------------------------------------------}
  1950. procedure TMMSpectrum.RefreshPCMData(PCMData: Pointer);
  1951. var
  1952.    Value: Longint;
  1953.    i: Integer;
  1954.    ReIndex: integer;
  1955.    Back1, Back2: Long;                       { Variables for differencing }
  1956.    {$IFDEF WIN32}
  1957.    fTemp : array[0..MAX_FFTLen-1] of Float;
  1958.    {$ELSE}
  1959.    fTemp : array[0..MAX_FFTLen-1] of Smallint;
  1960.    {$ENDIF}
  1961. begin
  1962.    if FEnabled and Visible and not FShowInfoHint then
  1963.    begin
  1964.       ReIndex := Ord(FChannel)-1;
  1965.       if (FDeriv = 0) then
  1966.       begin
  1967.          { perform windowing on sample Data from PCMData to FFFTData }
  1968.          if (FBits = b8bit) then
  1969.             if (FMode = mMono) then
  1970.             for i := 0 to FFTLen-1 do
  1971.             begin
  1972.                Value := PByteArray(PCMData)^[i];
  1973.                if Value >= 255 then PcmOverflow;
  1974.                fTemp[i] := MulDiv32(Value-128,FWinBuf^[i],128);
  1975.             end
  1976.             else if (FChannel = chBoth) then
  1977.             for i := 0 to FFTLen-1 do
  1978.             begin
  1979.                Value := (Word(PByteArray(PCMData)^[i+i])+PByteArray(PCMData)^[i+i+1])div 2;
  1980.                if Value >= 255 then PcmOverflow;
  1981.                fTemp[i] := MulDiv32(Value-128,FWinBuf^[i],128);
  1982.             end
  1983.             else
  1984.             for i := 0 to FFTLen-1 do
  1985.             begin
  1986.                Value := PByteArray(PCMData)^[i+i+ReIndex];
  1987.                if Value >= 255 then PcmOverflow;
  1988.                ftemp[i] := MulDiv32(Value-128,FWinBuf^[i],128);
  1989.             end
  1990.          else
  1991.             if (FMode = mMono) then
  1992.             for i := 0 to FFTLen-1 do
  1993.             begin
  1994.                Value := PSmallArray(PCMData)^[i];
  1995.                if Value >= 32767 then PcmOverflow;
  1996.                fTemp[i] := MulDiv32(Value,FWinBuf^[i],32768);
  1997.             end
  1998.             else if (FChannel = chBoth) then
  1999.             for i := 0 to FFTLen-1 do
  2000.             begin
  2001.                Value := (Long(PSmallArray(PCMData)^[i+i])+PSmallArray(PCMData)^[i+i+1])div 2;
  2002.                if Value >= 32766 then PcmOverflow;
  2003.                fTemp[i] := MulDiv32(Value,FWinBuf^[i],32768);
  2004.             end
  2005.             else
  2006.             for i := 0 to FFTLen-1 do
  2007.             begin
  2008.                Value := PSmallArray(PCMData)^[i+i+ReIndex];
  2009.                if Value >= 32767 then PcmOverflow;
  2010.                fTemp[i] := MulDiv32(Value,FWinBuf^[i],32768);
  2011.             end;
  2012.       end
  2013.       else if (FDeriv = 1) then
  2014.       begin
  2015.          { perform windowing on sample Data from PCMData to FFFTData }
  2016.          if (FBits = b8bit) then
  2017.          begin
  2018.             if (FMode = mMono) then
  2019.             begin
  2020.                Back1 := PByteArray(PCMData)^[0];
  2021.                for i := 0 to FFTLen-1 do
  2022.                begin
  2023.                   Value := PByteArray(PCMData)^[i];
  2024.                   if Value >= 255 then PcmOverflow;
  2025.                   fTemp[i] := MulDiv32(Value-Back1,FWinBuf^[i],128);
  2026.                   Back1 := Value;
  2027.                end;
  2028.             end
  2029.             else if (FChannel = chBoth) then
  2030.             begin
  2031.                Back1 := PByteArray(PCMData)^[0];
  2032.                for i := 0 to FFTLen-1 do
  2033.                begin
  2034.                   Value := (Word(PByteArray(PCMData)^[i+i])+PByteArray(PCMData)^[i+i+1])div 2;
  2035.                   if Value >= 255 then PcmOverflow;
  2036.                   fTemp[i] := MulDiv32(Value-Back1,FWinBuf^[i],128);
  2037.                   Back1 := Value;
  2038.                end;
  2039.             end
  2040.             else
  2041.             begin
  2042.                Back1 := PByteArray(PCMData)^[ReIndex];
  2043.                for i := 0 to FFTLen-1 do
  2044.                begin
  2045.                   Value := PByteArray(PCMData)^[i+i+ReIndex];
  2046.                   if Value >= 255 then PcmOverflow;
  2047.                   fTemp[i] := MulDiv32(Value-Back1,FWinBuf^[i],128);
  2048.                   Back1 := Value;
  2049.                end;
  2050.             end;
  2051.          end
  2052.          else
  2053.          begin
  2054.             if (FMode = mMono) then
  2055.             begin
  2056.                Back1 := PSmallArray(PCMData)^[0];
  2057.                for i := 0 to FFTLen-1 do
  2058.                begin
  2059.                   Value := PSmallArray(PCMData)^[i];
  2060.                   if Value >= 32767 then PcmOverflow;
  2061.                   fTemp[i] := MulDiv32(Value-Back1,FWinBuf^[i],32768);
  2062.                   Back1 := Value;
  2063.                end;
  2064.             end
  2065.             else if (FChannel = chBoth) then
  2066.             begin
  2067.                Back1 := PSmallArray(PCMData)^[0];
  2068.                for i := 0 to FFTLen-1 do
  2069.                begin
  2070.                   Value := (Long(PSmallArray(PCMData)^[i+i])+PSmallArray(PCMData)^[i+i+1])div 2;
  2071.                   if Value >= 32766 then PcmOverflow;
  2072.                   fTemp[i] := MulDiv32(Value-Back1,FWinBuf^[i],32768);
  2073.                   Back1 := Value;
  2074.                end;
  2075.             end
  2076.             else
  2077.             begin
  2078.                Back1 := PSmallArray(PCMData)^[ReIndex];
  2079.                for i := 0 to FFTLen-1 do
  2080.                begin
  2081.                   Value := PSmallArray(PCMData)^[i+i+ReIndex];
  2082.                   if Value >= 32767 then PcmOverflow;
  2083.                   fTemp[i] := MulDiv32(Value-Back1,FWinBuf^[i],32768);
  2084.                   Back1 := Value;
  2085.                end;
  2086.             end;
  2087.          end;
  2088.       end
  2089.       else { Deriv = 2 }
  2090.       begin
  2091.          { perform windowing on sample Data from PCMData to FFFTData }
  2092.          if (FBits = b8bit) then
  2093.          begin
  2094.             if (FMode = mMono) then
  2095.             begin
  2096.                Back1 := PByteArray(PCMData)^[0];
  2097.                Back2 := Back1;
  2098.                for i := 0 to FFTLen-1 do
  2099.                begin
  2100.                   Value := PByteArray(PCMData)^[i];
  2101.                   if Value >= 255 then PcmOverflow;
  2102.                   fTemp[i] := MulDiv32(Value-2*Back1+Back2,FWinBuf^[i],128);
  2103.                   Back2 := Back1;
  2104.                   Back1 := Value;
  2105.                end;
  2106.             end
  2107.             else if (FChannel = chBoth) then
  2108.             begin
  2109.                Back1 := PByteArray(PCMData)^[0];
  2110.                Back2 := Back1;
  2111.                for i := 0 to FFTLen-1 do
  2112.                begin
  2113.                   Value := (Word(PByteArray(PCMData)^[i+i])+PByteArray(PCMData)^[i+i+1])div 2;
  2114.                   if Value >= 255 then PcmOverflow;
  2115.                   fTemp[i] := MulDiv32(Value-2*Back1+Back2,FWinBuf^[i],128);
  2116.                   Back2 := Back1;
  2117.                   Back1 := Value;
  2118.                end;
  2119.             end
  2120.             else
  2121.             begin
  2122.                Back1 := PByteArray(PCMData)^[ReIndex];
  2123.                Back2 := Back1;
  2124.                for i := 0 to FFTLen-1 do
  2125.                begin
  2126.                   Value := PByteArray(PCMData)^[i+i+ReIndex];
  2127.                   if Value >= 255 then PcmOverflow;
  2128.                   fTemp[i] := MulDiv32(Value-2*Back1+Back2,FWinBuf^[i],128);
  2129.                   Back2 := Back1;
  2130.                   Back1 := Value;
  2131.                end;
  2132.             end;
  2133.          end
  2134.          else
  2135.          begin
  2136.             if (FMode = mMono) then
  2137.             begin
  2138.                Back1 := PSmallArray(PCMData)^[0];
  2139.                Back2 := Back1;
  2140.                for i := 0 to FFTLen-1 do
  2141.                begin
  2142.                   Value := PSmallArray(PCMData)^[i];
  2143.                   if Value >= 32767 then PcmOverflow;
  2144.                   fTemp[i] := MulDiv32(Value-2*Back1+Back2,FWinBuf^[i],32768);
  2145.                   Back2 := Back1;
  2146.                   Back1 := Value;
  2147.                end;
  2148.             end
  2149.             else if (FChannel = chBoth) then
  2150.             begin
  2151.                Back1 := PSmallArray(PCMData)^[0];
  2152.                Back2 := Back1;
  2153.                for i := 0 to FFTLen-1 do
  2154.                begin
  2155.                   Value := (Long(PSmallArray(PCMData)^[i+i])+PSmallArray(PCMData)^[i+i+1])div 2;
  2156.                   if Value >= 32767 then PcmOverflow;
  2157.                   fTemp[i] := MulDiv32(Value-2*Back1+Back2,FWinBuf^[i],32768);
  2158.                   Back2 := Back1;
  2159.                   Back1 := Value;
  2160.                end;
  2161.             end
  2162.             else
  2163.             begin
  2164.                Back1 := PSmallArray(PCMData)^[ReIndex];
  2165.                Back2 := Back1;
  2166.                for i := 0 to FFTLen-1 do
  2167.                begin
  2168.                   Value := PSmallArray(PCMData)^[i+i+ReIndex];
  2169.                   if Value >= 32767 then PcmOverflow;
  2170.                   fTemp[i] := MulDiv32(Value-2*Back1+Back2,FWinBuf^[i],32768);
  2171.                   Back2 := Back1;
  2172.                   Back1 := Value;
  2173.                end;
  2174.             end;
  2175.          end;
  2176.       end;
  2177.       { calc the FFT }
  2178.       {$IFDEF WIN32}
  2179.       DoRealFFT(FpFFT,@fTemp, 1);
  2180.       for i := 0 to FFTLen-1 do FFFTData^[i] := Trunc(fTemp[i]/(FFTLen div 2));
  2181.       {$ELSE}
  2182.       for i := 0 to FFTLen-1 do FFFTData^[i] := fTemp[i];
  2183.       FFT.CalcFFT(Pointer(FFFTData));
  2184.       {$ENDIF}
  2185.       { calc the magnitude }
  2186.       CalcMagnitude(False);
  2187.       { next, put this data up on the display }
  2188.       FastDraw(DrawSpectrum,False);
  2189.    end;
  2190. end;
  2191. {-- TMMSpectrum ---------------------------------------------------------}
  2192. procedure TMMSpectrum.RefreshFFTData(FFTData: Pointer);
  2193. begin
  2194.    Move(PByte(FFTData)^, FFFTData^, FFTLen*sizeOf(SmallInt));
  2195.    { calc the magnitude }
  2196.    CalcMagnitude(False);
  2197.    { next, put this data up on the display }
  2198.    FastDraw(DrawSpectrum,False);
  2199. end;
  2200. {-- TMMSpectrum ---------------------------------------------------------}
  2201. procedure TMMSpectrum.RefreshMagnitudeData(MagData: Pointer);
  2202. begin
  2203.    Move(PChar(MagData)^, FFFTData^, (FFTLen div 2)*sizeOf(Longint));
  2204.    { calc display values }
  2205.    CalcMagnitude(True);
  2206.    { next, put this data up on the display }
  2207.    FastDraw(DrawSpectrum,False);
  2208. end;
  2209. {-- TMMSpectrum ---------------------------------------------------------}
  2210. procedure TMMSpectrum.CalcMagnitude(MagnitudeForm: Boolean);
  2211. var
  2212.    i: integer;
  2213.    re,im: Long;
  2214.    a2,Root: Long;{ Variables for computing Sqrt/Log of Amplitude^2 }
  2215. begin
  2216.    { go through the data set and convert it to magnitude form }
  2217.    if (FDecayMode <> dmNone) or  (not FLogAmp) then
  2218.    begin
  2219.       { Use sqrt(a2) in averaging mode and linear-amplitude mode }
  2220.       inc(FDecayPtr);
  2221.       inc(FDecayCntAct);
  2222.       if (FDecayPtr >= FDecayCount) then FDecayPtr := 0;
  2223.       if (FDecayCntAct > FDecayCount) then FDecayCntAct := FDecayCount;
  2224.       for i := 0 to (FFTLen div 2)-1 do
  2225.       begin
  2226.          if MagnitudeForm then
  2227.          begin
  2228.             Root := PLongArray(FFFTData)^[i];
  2229.          end
  2230.          else
  2231.          begin
  2232.             { Compute the magnitude }
  2233.             {$IFDEF WIN32}
  2234.             re := FFFTData^[i+i];
  2235.             im := FFFTData^[i+i+1];
  2236.             {$ELSE}
  2237.             re := FFFTData^[FFT.BitReversed^[i]];
  2238.             im := FFFTData^[FFT.BitReversed^[i]+1];
  2239.             {$ENDIF}
  2240.             a2 := re*re+im*im;
  2241.             { Watch for possible overflow }
  2242.             if (a2 < 0) then a2 := 0;
  2243.             { Use higher resolution only for small values }
  2244.             {$IFDEF USE_INTEGER_CODE}
  2245.     if (a2 > 4194304) then
  2246.     begin
  2247.        Root := 32;
  2248.        repeat
  2249.            Mask :=a2 div Root;
  2250.            Root := (Root+Mask) shr 1;
  2251.                until not (abs(Root-Mask) > 1);
  2252.        Root := Root*16;
  2253.             end
  2254.             else
  2255.     begin
  2256.        Root := 512;
  2257.        a2 := a2*256;
  2258.                repeat
  2259.            Mask := a2 div Root;
  2260.            Root := (Root+Mask) shr 1;
  2261.                until not (abs(root-mask) > 1);
  2262.             end;
  2263.             {$ELSE}
  2264.             Root := Trunc(sqrt(a2)*16);
  2265.             {$ENDIF}
  2266.          end;
  2267.  { In decay mode, need to average this value }
  2268.  case Ord(FDecayMode) of
  2269.     1: begin
  2270.                FLastVal_F^[i] := FLastVal_F^[i]*FDecayFactor;
  2271.                   if (Root >= FLastVal_F^[i]) then FLastVal_F^[i] := Root
  2272.                   else Root := Trunc(FLastVal_F^[i]);
  2273.                end;
  2274.             2: begin
  2275.   FLastVal_F^[i] := FLastVal_F^[i]*FDecayFactor+(1-FDecayFactor)*Root;
  2276.   Root := Floor(FLastVal_F^[i]);
  2277.                end;
  2278.             3: begin
  2279.                   FLastVal^[i] := FLastVal^[i] + (Root-FDataBuf^[FDecayPtr]^[i]);
  2280.                   FDataBuf^[FDecayPtr]^[i] := Root;
  2281.                   Root := FLastVal^[i] div FDecayCntAct;
  2282.                end;
  2283.          end;
  2284.  FDisplayVal^[i] := Root;
  2285.       end;
  2286.    end
  2287.    else { No averaging, log-amplitude mode }
  2288.    begin
  2289.       for i := 0 to (FFTLen div 2)-1 do
  2290.       begin
  2291.          if MagnitudeForm then
  2292.          begin
  2293.             Root := PLongArray(FFFTData)^[i];
  2294.          end
  2295.          else
  2296.          begin
  2297.             { Compute the magnitude }
  2298.             {$IFDEF WIN32}
  2299.             re := FFFTData^[i+i];
  2300.             im := FFFTData^[i+i+1];
  2301.             {$ELSE}
  2302.             re := FFFTData^[FFT.BitReversed^[i]];
  2303.             im := FFFTData^[FFT.BitReversed^[i]+1];
  2304.             {$ENDIF}
  2305.             a2 := re*re+im*im;
  2306.             { Watch for possible overflow }
  2307.             if (a2 < 0) then a2 := 0;
  2308.             {$IFDEF USE_INTEGER_CODE}
  2309.     Root := 32768;
  2310.     while (a2 >= 32) do
  2311.     begin
  2312.        Root := Root + 8192;
  2313.        a2 := a2 shr 1;
  2314.     end;
  2315.     Root := Root + _ln[a2]-FYBase^[i];
  2316.             {$ELSE}
  2317.     if (a2 > 0) then Root := Trunc(log(a2)*FLogScaleFactor-FYBase^[i])
  2318.     else Root := 0;
  2319.             {$ENDIF}
  2320.          end;
  2321.          if (Root < 0) then
  2322.             FDisplayVal^[i] := 0
  2323.          else
  2324.          FDisplayVal^[i] := Root;
  2325.       end;
  2326.    end;
  2327. end;
  2328. {-- TMMSpectrum ---------------------------------------------------------}
  2329. procedure TMMSpectrum.CalcDisplayValues;
  2330. var
  2331.    i, j, index, xval: integer;
  2332.    dv: Long;
  2333. begin
  2334.    dv := 0;
  2335.    j := 0;
  2336.    { In log-amp decay mode, need to do some special things }
  2337.    if (FDecayMode <> dmNone) and FLogAmp then
  2338.    begin
  2339.       i := 0;
  2340.       while i < FWidth do
  2341.       begin
  2342.          { If this line is the same as the previous one, just use the
  2343.            previous Y value.  Else go ahead and compute the value. }
  2344.          index := Fx1^[i];
  2345.          if (index <> -1) then
  2346.          begin
  2347.             if i > 0 then
  2348.             begin
  2349.                { save the display rect for this set of bins }
  2350.                FDrawVal^[j].Right := i;
  2351.                FDrawVal^[j].Value := dv;
  2352.                inc(j);
  2353.             end;
  2354.             FDrawVal^[j].Left := i;
  2355.     { Convert the amplitude values to log scale }
  2356.     dv := FDisplayVal^[index];
  2357.             if (Fx2^[i] > 0) then { Take the max of a set of bins }
  2358.             begin
  2359.                xval := index;
  2360.                while xval < Fx2^[i] do
  2361.        begin
  2362.           if (FDisplayVal^[xval] > dv) then
  2363.                   begin
  2364.      dv := FDisplayVal^[xval];
  2365.                      index := xval;
  2366.                   end;
  2367.                   inc(xval);
  2368.                end;
  2369.             end;
  2370.             if (dv > 0) then
  2371.                 dv := Trunc((log(dv/16.0)*2*FLogScaleFactor-FYBase^[index])
  2372.                              * FDispScaleFactor)
  2373.             else dv := 0;
  2374.             { new peak found ? }
  2375.             if (dv > FPeak.Amplitude) then
  2376.             begin
  2377.        FPeak.Amplitude := dv;
  2378.                FPeak.Index := Fx1^[i];
  2379.                FPeak.X := i;
  2380.             end;
  2381.             if dv >= FHeight then GainOverflow;
  2382.          end;
  2383.          inc(i);
  2384.       end;
  2385.    end
  2386.    else
  2387.    begin
  2388.       { For linear amplitude mode and log amp without decay }
  2389.       i := 0;
  2390.       while i < FWidth do
  2391.       begin
  2392.          { If this line is the same as the previous one, just use the previous
  2393.            Y value. Else go ahead and compute the value. }
  2394.          index := Fx1^[i];
  2395.          if (index <> -1) then
  2396.          begin
  2397.             if i > 0 then
  2398.             begin
  2399.                { save the display rect for this set of bins }
  2400.                FDrawVal^[j].Right := i;
  2401.                FDrawVal^[j].Value := dv;
  2402.                { now the next rect }
  2403.                inc(j);
  2404.             end;
  2405.             FDrawVal^[j].Left := i;
  2406.     dv := FDisplayVal^[index];
  2407.     if (Fx2^[i] > 0) then { Take the maximum of a set of bins }
  2408.     begin
  2409.        while (index < Fx2^[i]) do
  2410.                begin
  2411.           if (FDisplayVal^[index] > dv) then dv := FDisplayVal^[index];
  2412.                   inc(index);
  2413.                end;
  2414.             end;
  2415.             if (dv > 0) then dv := (dv * FYScale^[i]) shr FShift
  2416.             else dv := 0;
  2417.             { new peak found ? }
  2418.             if (dv > FPeak.Amplitude) then
  2419.             begin
  2420.        FPeak.Amplitude := dv;
  2421.        FPeak.Index := Fx1^[i];
  2422.                FPeak.X := i;
  2423.             end;
  2424.             if dv >= FHeight then GainOverflow;
  2425.          end;
  2426.          inc(i);
  2427.       end;
  2428.    end;
  2429.    { save the last value }
  2430.    FDrawVal^[j].Right := i;
  2431.    FDrawVal^[j].Value := dv;
  2432.    { and mark the end }
  2433.    FDrawVal^[j+1].Left := -1;
  2434. end;
  2435. {-- TMMSpectrum ---------------------------------------------------------}
  2436. procedure TMMSpectrum.SetOnDrawBar(aValue: TMMSpectrumDrawBar);
  2437. begin
  2438.    FOnDrawBar := aValue;
  2439.    if not assigned(FOnDrawBar) then DrawInactiveSpots;
  2440.    Invalidate;
  2441. end;
  2442. {-- TMMSpectrum ---------------------------------------------------------}
  2443. procedure TMMSpectrum.SetLocalVariables(DIB: TMMDIBCanvas);
  2444. begin
  2445.    with DIB do
  2446.    begin
  2447.       _DIB := DIB;
  2448.       _Bar1Color  := DIB_ColorToIndex(FBar1Color);
  2449.       _Bar2Color  := DIB_ColorToIndex(FBar2Color);
  2450.       _Bar3Color  := DIB_ColorToIndex(FBar3Color);
  2451.       _Inact1Color := DIB_ColorToIndex(FInact1Color);
  2452.       _Inact2Color := DIB_ColorToIndex(FInact2Color);
  2453.       _Inact3Color := DIB_ColorToIndex(FInact3Color);
  2454.       _NumSpots   := FNumSpots;
  2455.       _NumPeaks   := FNumPeaks;
  2456.       _SpotHeight := FSpotHeight;
  2457.       _SpotSpace  := FSpotSpace;
  2458.       _FirstSpace := FFirstSpace;
  2459.       _Space      := FSpace;
  2460.       _Point1Spot := FPoint1Spot;
  2461.       _Point2Spot := FPoint2Spot;
  2462.       _ActiveDoted  := FActiveDoted;
  2463.       _InactiveDoted:= FInactiveDoted;
  2464.       _DrawInactive := FDrawInactive;
  2465.       _Offset := 1;
  2466.       if (FKind = skScroll) then _Offset := FHeight-FHeight div 3;
  2467.    end;
  2468. end;
  2469. {-- TMMSpectrum ---------------------------------------------------------}
  2470. procedure TMMSpectrum.InitLocalVariables;
  2471. begin
  2472.    { copy some variables from the DIBCanvas unit to this scope to fix a bug in CBuilder 3.0 }
  2473.    _DIB_ORIENT   := DIB_ORIENT;
  2474.    _biBits       := biBits;
  2475.    _biBPP        := biBPP;
  2476.    _biWidth      := biWidth;
  2477.    _biHeight     := biHeight;
  2478.    _biScanWidth  := biScanWidth;
  2479.    _biLineDiff   := biLineDiff;
  2480.    _biColor      := biColor;
  2481.    _biSurface    := biSurface;
  2482.    _biPenPos     := biPenPos;
  2483.    _biClipRect   := biClipRect;
  2484. end;
  2485. {-- TMMSpectrum ---------------------------------------------------------}
  2486. procedure TMMSpectrum.DrawAsDots;
  2487. var
  2488.    i, Y: integer;
  2489. begin
  2490.    SetLocalVariables(DIBCanvas);
  2491.    with DIBCanvas do
  2492.    begin
  2493.       i := 0;
  2494.       while (FDrawVal^[i].Left <> -1) and (i < FWidth) do
  2495.       with FDrawVal^[i] do
  2496.       begin
  2497.          Y := MinMax(FHeight-Value-1,0,FHeight-1);
  2498.          if not FEnabled then DIB_SetTColor(_Inact1Color)
  2499.          else if Y <= _Point2Spot then DIB_SetColor(_Bar3Color)
  2500.          else if Y <= _Point1Spot then DIB_SetColor(_Bar2Color)
  2501.          else DIB_SetColor(_Bar1Color);
  2502.          DIB_HLine(Left, Right, Y);
  2503.          inc(i);
  2504.       end;
  2505.    end;
  2506. end;
  2507. {-- TMMSpectrum ---------------------------------------------------------}
  2508. procedure TMMSpectrum.DrawAsLines;
  2509. var
  2510.    i,y: integer;
  2511. begin
  2512.    SetLocalVariables(DIBCanvas);
  2513.    with DIBCanvas do
  2514.    begin
  2515.       if FEnabled then DIB_SetTColor(FBar1Color)
  2516.       else DIB_SetTColor(FInact1Color);
  2517.       DIB_MoveTo(0,MinMax(FHeight-FDrawVal^[0].Value-_Offset,0,FHeight-1));
  2518.       {$IFDEF USEASM}
  2519.       _biPenPos := biPenPos;
  2520.       {$ENDIF}
  2521.       i := 0;
  2522.       y := 0;
  2523.       while (FDrawVal^[i].Left <> -1) and (i < FWidth) do
  2524.       with FDrawVal^[i] do
  2525.       begin
  2526.          y := MinMax(FHeight-Value-_Offset,0,FHeight-1);
  2527.          {$IFDEF USEASM}
  2528.          if (BitsPerPixel <> 24) then
  2529.              PointedLineTo(Left+(Right-Left) div 2, y,(FKind = skScroll))
  2530.          else
  2531.              DIB_LineTo(Left+(Right-Left) div 2, y);
  2532.          {$ELSE}
  2533.          DIB_LineTo(Left+(Right-Left) div 2, y);
  2534.          {$ENDIF}
  2535.          inc(i);
  2536.       end;
  2537.       {$IFDEF USEASM}
  2538.       biPenPos := _biPenPos;
  2539.       {$ENDIF}
  2540.       if (BitsPerPixel <> 24) then DIB_LineTo(FWidth,y);
  2541.       if (FKind <> skScroll) and (FNumPeaks > 0) then
  2542.       begin
  2543.          i := 0;
  2544.          while (FDrawVal^[i].Left <> -1) and (i < FWidth) do
  2545.          with FDrawVal^[i] do
  2546.          begin
  2547.             if (Value >= Peak) and (Value > 0) then
  2548.             begin
  2549.                Peak := Value;
  2550.                PeakCnt := (FPeakDelay*2)+1;
  2551.             end;
  2552.             y := MinMax(FHeight-Peak-_Offset,0,FHeight-1);
  2553.             if (i = 0) then
  2554.             begin
  2555.                DIB_MoveTo(0,y);
  2556.                {$IFDEF USEASM}
  2557.                _biPenPos := biPenPos;
  2558.                {$ENDIF}
  2559.             end;
  2560.             {$IFDEF USEASM}
  2561.             if (BitsPerPixel <> 24) then
  2562.                 PointedLineTo(Left+(Right-Left) div 2, y,(FKind = skScroll))
  2563.             else
  2564.                 DIB_LineTo(Left+(Right-Left) div 2, y);
  2565.             {$ELSE}
  2566.             DIB_LineTo(Left+(Right-Left) div 2, y);
  2567.             {$ENDIF}
  2568.             inc(i);
  2569.          end;
  2570.          {$IFDEF USEASM}
  2571.          biPenPos := _biPenPos;
  2572.          {$ENDIF}
  2573.          DIB_LineTo(FWidth,y);
  2574.       end;
  2575.    end;
  2576. end;
  2577. {-- TMMSpectrum ---------------------------------------------------------}
  2578. procedure TMMSpectrum.DrawAsVLines;
  2579. var
  2580.    aRect: TRect;
  2581.    i, Y: integer;
  2582. begin
  2583.    SetLocalVariables(DIBCanvas);
  2584.    with DIBCanvas do
  2585.    begin
  2586.       i := 0;
  2587.       while (FDrawVal^[i].Left <> -1) and (i < FWidth) do
  2588.       with FDrawVal^[i] do
  2589.       begin
  2590.          Value := MinMax(Value,0,FHeight-1);
  2591.          Y := (FHeight + Value) div 2;
  2592.          aRect.Top := FHeight-Y-1;
  2593.          aRect.Bottom := Y+1;
  2594.          aRect.Left := Left;
  2595.          aRect.Right := Right;
  2596.          with aRect do
  2597.          begin
  2598.             if Right - Left <= 0 then Right := Left + 1;{ we don't accept <= 0 }
  2599.             if Right - Left > _Space then               { can we work with space ? }
  2600.             begin
  2601.                Left := Left + _Space div 2;
  2602.                Right := (Right + _Space div 2) - _Space;
  2603.             end;
  2604.             if FEnabled then
  2605.             begin
  2606.                if Value > _Point2Spot then
  2607.                begin
  2608.                   DIB_SetColor(_Bar3Color);
  2609.                   DIB_FillRect(aRect);
  2610.                   InflateRect(aRect,0,-(Value-_Point2Spot)div 2);
  2611.                   dec(Value,Value-_Point2Spot);
  2612.                end;
  2613.                if Value > _Point1Spot then
  2614.                begin
  2615.                   DIB_SetColor(_Bar2Color);
  2616.                   DIB_FillRect(aRect);
  2617.                   InflateRect(aRect,0,-(Value-_Point1Spot)div 2);
  2618.                end;
  2619.                DIB_SetColor(_Bar1Color);
  2620.                DIB_FillRect(aRect);
  2621.             end
  2622.             else
  2623.             begin
  2624.                DIB_SetColor(_Inact1Color);
  2625.                DIB_FillRect(aRect);
  2626.             end;
  2627.          end;
  2628.          inc(i);
  2629.       end;
  2630.    end;
  2631. end;
  2632. {-- TMMSpectrum ---------------------------------------------------------}
  2633. procedure TMMSpectrum.DrawAsBars;
  2634. var
  2635.    i: integer;
  2636.    nSpots,iMax: integer;
  2637. begin
  2638.    SetLocalVariables(DIBCanvas);
  2639.    if assigned(FOnDrawBar) then
  2640.       iMax := FHeight
  2641.    else
  2642.       iMax := FNumSpots;
  2643.    i := 0;
  2644.    while (FDrawVal^[i].Left <> -1) and (i < FWidth) do
  2645.    with FDrawVal^[i] do
  2646.    begin
  2647.       nSpots := MinMax(Round(Value/(FHeight/iMax)+0.5),0,iMax);
  2648.       if (nSpots >= Peak) and (nSpots > 0) and (FNumPeaks > 0) then
  2649.       begin
  2650.          Peak := nSpots;
  2651.          PeakCnt := (FPeakDelay*2)+1;
  2652.       end;
  2653.       if assigned(FOnDrawBar) then
  2654.          FOnDrawBar(Self,_DIB,Rect(Left,0,Right,FHeight),nSpots,Peak)
  2655.       else if (FKind = skBars) then
  2656.       begin
  2657.          {$IFDEF USEASM}
  2658.          if (BitsPerPixel <> 24) then
  2659.              DrawBar(Left,Right,nSpots,Peak)
  2660.          else
  2661.              DrawBar_Native(Left,Right,nSpots,Peak);
  2662.          {$ELSE}
  2663.          DrawBar_Native(Left,Right,nSpots,Peak);
  2664.          {$ENDIF}
  2665.       end
  2666.       else
  2667.       begin
  2668.          {$IFDEF USEASM}
  2669.          if (BitsPerPixel <> 24) then
  2670.              DrawBarPeak(Left,Right,nSpots,Peak)
  2671.          else
  2672.              DrawBarPeak_Native(Left,Right,nSpots,Peak);
  2673.          {$ELSE}
  2674.          DrawBarPeak_Native(Left,Right,nSpots,Peak);
  2675.          {$ENDIF}
  2676.       end;
  2677.       inc(i);
  2678.    end;
  2679. end;
  2680. {-- TMMSpectrum ---------------------------------------------------------}
  2681. procedure TMMSpectrum.DrawInactiveSpots;
  2682. var
  2683.    i, L: integer;
  2684. begin
  2685.    if not (csLoading in ComponentState) and not (csCreating in ControlState) and
  2686.       not assigned(FOnDrawBar) and ((FKind = skBars) or (FKind = skPeaks)) then
  2687.    begin
  2688.       SetLocalVariables(FBarDIB);
  2689.       _Bar1Color  := _Inact1Color;
  2690.       _Bar2Color  := _Inact2Color;
  2691.       _Bar3Color  := _Inact3Color;
  2692.       _ActiveDoted := _InactiveDoted;
  2693.       with _DIB do
  2694.       begin
  2695.          DIB_InitDrawing;
  2696.          InitLocalVariables;
  2697.          DIB_SetTColor(Color);
  2698.          DIB_Clear;
  2699.          L := 0;
  2700.          for i := 0 to FWidth-1 do
  2701.          begin
  2702.             { If this line is the same as the previous one, just use the
  2703.               previous Y value.  Else go ahead and compute the value. }
  2704.             if (Fx1^[i] <> -1) then
  2705.             begin
  2706.                { draw this bar }
  2707.                if i > 0 then
  2708.                begin
  2709.                   {$IFDEF USEASM}
  2710.                   if (BitsPerPixel <> 24) then
  2711.                       DrawBar(L,i,FNumSpots,0)
  2712.                   else
  2713.                       DrawBar_Native(L,i,FNumSpots,0);
  2714.                   {$ELSE}
  2715.                   DrawBar_Native(L,i,FNumSpots,0);
  2716.                   {$ENDIF}
  2717.                end;
  2718.                L := i;
  2719.             end;
  2720.          end;
  2721.          {$IFDEF USEASM}
  2722.          if (BitsPerPixel <> 24) then
  2723.              DrawBar(L,FWidth,FNumSpots,0)
  2724.          else
  2725.             DrawBar_Native(L,FWidth,FNumSpots,0);
  2726.          {$ELSE}
  2727.          DrawBar_Native(L,FWidth,FNumSpots,0);
  2728.          {$ENDIF}
  2729.          DIB_DoneDrawing;
  2730.       end;
  2731.    end;
  2732. end;
  2733. {$IFDEF USEASM}
  2734. {$IFDEF WIN32}{$L MMSPEC32.OBJ}{$ELSE}{$L MMSPEC16.OBJ}{$ENDIF}
  2735. {$F+}
  2736. procedure TMMSpectrum.DrawBar(X1,X2,nSpots, Peak: integer); external;
  2737. procedure TMMSpectrum.DrawBarPeak(X1,X2, nSpots, Peak: integer); external;
  2738. procedure TMMSpectrum.PointedLineTo(X,Y: integer; Pointed: Boolean); external;
  2739. {$F-}
  2740. {$ENDIF}
  2741. {-- TMMSpectrum ---------------------------------------------------------}
  2742. procedure TMMSpectrum.DrawBar_Native(X1, X2, nSpots, Peak: integer);
  2743. Var
  2744.    SpotRect: TRect;                                 { Spot draw rectangle }
  2745.    i,SpotInc: integer;
  2746. begin
  2747.    SpotInc    := FSpotHeight + FSpotSpace;
  2748.    if X2 - X1 <= 0 then X2 := X1 + 1;              { we don't accept <= 0 }
  2749.    if X2 - X1 > FSpace then                    { can we work with space ? }
  2750.    begin
  2751.       X1 := X1 + FSpace div 2;
  2752.       X2 := (X2 + FSpace div 2) - FSpace;
  2753.    end;
  2754.    SpotRect.Left := X1;
  2755.    SpotRect.Right := X2;
  2756.    SpotRect.Bottom := FHeight - _FirstSpace;
  2757.    SpotRect.Top := SpotRect.Bottom - _SpotHeight;
  2758.    with _DIB do
  2759.    begin
  2760.       DIB_SetColor(_Bar1Color);
  2761.       for i := 1 to nSpots do                  { draw the highlited spots }
  2762.       begin
  2763.          if i > _Point2Spot then DIB_SetColor(_Bar3Color)
  2764.          else if i > _Point1Spot then DIB_SetColor(_Bar2Color);
  2765.          DIB_FillRectDoted(SpotRect,_ActiveDoted);
  2766.          OffsetRect(SpotRect, 0, -SpotInc);
  2767.       end;
  2768.       if (_NumPeaks > 0) and (Peak > nSpots) then
  2769.       begin
  2770.          OffsetRect(SpotRect, 0, -((Peak-1)-nSpots)*SpotInc);
  2771.          for i := 0 to _NumPeaks-1 do          { draw the peak spots }
  2772.          begin
  2773.             if Peak-i  > _Point2Spot then DIB_SetColor(_Bar3Color)
  2774.             else if Peak-i > _Point1Spot then DIB_SetColor(_Bar2Color)
  2775.             else DIB_SetColor(_Bar1Color);
  2776.             DIB_FillRectDoted(SpotRect,_ActiveDoted);
  2777.             OffsetRect(SpotRect, 0, SpotInc);
  2778.          end;
  2779.       end;
  2780.    end;
  2781. end;
  2782. {-- TMMSpectrum ---------------------------------------------------------}
  2783. procedure TMMSpectrum.DrawBarPeak_Native(X1, X2, nSpots, Peak: integer);
  2784. Var
  2785.    SpotRect: TRect;                                 { Spot draw rectangle }
  2786.    i,SpotInc: integer;
  2787. begin
  2788.    if X2 - X1 <= 0 then X2 := X1 + 1;              { we don't accept <= 0 }
  2789.    if X2 - X1 > FSpace then                    { can we work with space ? }
  2790.    begin
  2791.       X1 := X1 + FSpace div 2;
  2792.       X2 := (X2 + FSpace div 2) - FSpace;
  2793.    end;
  2794.    SpotInc := FSpotHeight + FSpotSpace;
  2795.    SpotRect.Left := X1;
  2796.    SpotRect.Right := X2;
  2797.    with DIBCanvas do
  2798.    begin
  2799.       if (nSpots > 0) then
  2800.       begin
  2801.          SpotRect.Bottom := FHeight - _FirstSpace - ((nSpots-1)*SpotInc);
  2802.          SpotRect.Top := SpotRect.Bottom - _SpotHeight;
  2803.          if nSpots > _Point2Spot then DIB_SetColor(_Bar3Color)
  2804.          else if nSpots > _Point1Spot then DIB_SetColor(_Bar2Color)
  2805.          else DIB_SetColor(_Bar1Color);
  2806.          DIB_FillRectDoted(SpotRect,_ActiveDoted);
  2807.       end;
  2808.       if (_NumPeaks > 0) and (Peak > nSpots) then
  2809.       begin
  2810.          SpotRect.Bottom := FHeight - _FirstSpace - (Peak-1)*SpotInc;
  2811.          SpotRect.Top := SpotRect.Bottom - _SpotHeight;
  2812.          for i := 0 to _NumPeaks-1 do             { draw the peak spots }
  2813.          begin
  2814.             if Peak-i  > FPoint2Spot then DIB_SetColor(_Bar3Color)
  2815.             else if Peak-i > FPoint1Spot then DIB_SetColor(_Bar2Color)
  2816.             else DIB_SetColor(_Bar1Color);
  2817.             DIB_FillRectDoted(SpotRect,_ActiveDoted);
  2818.             OffsetRect(SpotRect, 0, SpotInc);
  2819.          end;
  2820.       end;
  2821.    end;
  2822. end;
  2823. {-- TMMSpectrum ---------------------------------------------------------}
  2824. procedure TMMSpectrum.DrawPeakValue;
  2825. var
  2826.    Border,Y: integer;
  2827.    Text: String;
  2828. begin
  2829.    if not (csDesigning in ComponentState) then
  2830.    begin
  2831.       if FDisplayPeak and (FPeak.Amplitude > 5) then
  2832.       with Canvas, FPeak do
  2833.       begin
  2834.          Font.Name := 'MS Sans Serif';
  2835.          Font.Style := [fsBold];
  2836.          Font.Size := 8;
  2837.          Font.Color := clWhite;
  2838.          Brush.Style := bsClear;
  2839.          if FLogFreq then
  2840.          begin
  2841.             if index <= 1 then Freq := (index+0.25) * FSampleRate/FFTLen
  2842.             else Freq := index * FSampleRate/FFTLen;
  2843.          end
  2844.          else Freq := (index+0.5) * FSampleRate/FFTLen;
  2845.          Text := TrimLeft(Format('%7.1f Hz',[Freq]));
  2846.          if X + TextWidth(Text) >= FWidth then X := FWidth-TextWidth(Text)-1;
  2847.          Y := Max((FHeight-Amplitude-_Offset)-TextHeight(Text),0);
  2848.          Border := BevelExtend;
  2849.          if FDrawAmpScale then inc(Border, SCALEWIDTH);
  2850.          TextOut(Border+X,BevelExtend+Y,Text);
  2851.          Font.Style := [];
  2852.          Brush.Style := bsSolid;
  2853.       end;
  2854.    end;
  2855. end;
  2856. {$IFDEF WIN32}
  2857. {-- TMMSpectrum --------------------------------------------------------}
  2858. procedure TMMSpectrum.DrawInfo(Pos: TPoint);
  2859. var
  2860.    Freq, Amp, Text: String;
  2861.    aRect: TRect;
  2862.    Buf: array[0..255] of char;
  2863.    DC: HDC;
  2864.    WindowHandle: HWND;
  2865. begin
  2866.    if FShowInfoHint then
  2867.    with DIBCanvas do
  2868.    begin
  2869.       if PtInRect(FClientRect,Pos) then
  2870.       begin
  2871.          Freq := Format('%2.3f KHz', [GetFrequencyAtPos(Pos)/1000]);
  2872.          if (FKind <> skScroll) then
  2873.          begin
  2874.             if FLogAmp then
  2875.                Amp := Format(' %2.1f dB',[GetAmplitudeAtPos(Pos)])
  2876.             else
  2877.             begin
  2878.                if (VerticalScale > 9) then
  2879.                   Amp := Format(' %4.2f V',[GetAmplitudeAtPos(Pos)])
  2880.                else
  2881.                   Amp := Format(' %5.3f V',[GetAmplitudeAtPos(Pos)]);
  2882.             end;
  2883.          end
  2884.          else Amp := '';
  2885.          Font.Name := 'MS Sans Serif';
  2886.          Font.Style := [];
  2887.          Font.Size := 8;
  2888.          {$IFDEF WIN32}
  2889.          Font.Color := clInfoText;
  2890.          {$ELSE}
  2891.          Font.Color := clBlack;
  2892.          {$ENDIF}
  2893.          Text := Freq+Amp;
  2894.          aRect.Left := Pos.X-BevelExtend;
  2895.          if FDrawAmpScale then dec(aRect.Left, SCALEWIDTH);
  2896.          aRect.Top := Pos.Y-BevelExtend+15;
  2897.          aRect.Right := aRect.Left + TextWidth(Text)+4;
  2898.          aRect.Bottom := aRect.Top + TextHeight(Text)+2;
  2899.          if (aRect.Bottom > FHeight) then OffsetRect(aRect,0,-40);
  2900.          if (aRect.Right > FWidth) then OffsetRect(aRect,FWidth-aRect.Right,0);
  2901.          if (aRect.Top < 0) then
  2902.          begin
  2903.             aRect.Top := 0;
  2904.             aRect.Bottom := TextHeight(Text)+2;
  2905.          end;
  2906.          if (SaveDC = 0) then
  2907.          begin
  2908.             { create memory DC for save bitmap }
  2909.             SaveDC := CreateCompatibleDC(DIBCanvas.Handle);
  2910.             { create bitmap to store background }
  2911.             SaveWidth := 10*TextWidth('W')+4;
  2912.             SaveHeight := TextHeight('W')+2;
  2913.             SaveBitmap := CreateCompatibleBitmap(DIBCanvas.Handle,SaveWidth,SaveHeight);
  2914.             OldBitmap := SelectObject(SaveDC, SaveBitmap);
  2915.          end
  2916.          else
  2917.             { restore background }
  2918.             BitBlt(DIBCanvas.Handle,SaveInfoPos.X,
  2919.                    SaveInfoPos.Y,SaveWidth,SaveHeight,
  2920.                    SaveDC,0,0,SRCCOPY);
  2921.          { save background }
  2922.          BitBlt(SaveDC,0,0,SaveWidth,SaveHeight,
  2923.                 DIBCanvas.Handle,aRect.Left,aRect.Top,SRCCOPY);
  2924.          SaveInfoPos := aRect.TopLeft;
  2925.          Brush.Color := INFOCOLOR;
  2926.          Brush.Style := bsSolid;
  2927.          Pen.Color := clBlack;
  2928.          Rectangle(aRect.Left,aRect.Top,aRect.Right,aRect.Bottom);
  2929.          Brush.Style := bsClear;
  2930.          DrawText(Handle, StrPCopy(Buf, Text), -1, aRect,
  2931.                   DT_SINGLELINE  or DT_VCENTER or DT_CENTER or DT_NOPREFIX);
  2932.          Brush.Style := bsSolid;
  2933.       end
  2934.       else if (SaveBitmap <> 0) then
  2935.       begin
  2936.          { restore background }
  2937.          BitBlt(DIBCanvas.Handle,SaveInfoPos.X,
  2938.                 SaveInfoPos.Y,SaveWidth,SaveHeight,
  2939.                 SaveDC,0,0,SRCCOPY);
  2940.       end;
  2941.       DIB_InitDrawing;                                  { copy to screen }
  2942.       DC := GetDeviceContext(WindowHandle);
  2943.       DIBCanvas.DIB_BitBlt(DC, FClientRect,0,0);
  2944.       ReleaseDC(WindowHandle, DC);
  2945.       DIB_DoneDrawing;
  2946.       DrawPeakValue;
  2947.    end;
  2948. end;
  2949. {$ELSE}
  2950. {-- TMMSpectrum --------------------------------------------------------}
  2951. procedure TMMSpectrum.DrawInfo(Pos: TPoint);
  2952. var
  2953.    Freq, Amp, Text: String;
  2954.    aRect: TRect;
  2955.    Buf: array[0..255] of char;
  2956.    Border: integer;
  2957. begin
  2958.    if FShowInfoHint then
  2959.    with Canvas do
  2960.    begin
  2961.       if PtInRect(FClientRect,Pos) then
  2962.       begin
  2963.          Freq := Format('%2.3f KHz', [GetFrequency(Pos)/1000]);
  2964.          if (FKind <> skScroll) then
  2965.          begin
  2966.             if FLogAmp then
  2967.                Amp := Format(' %2.1f dB',[GetAmplitude(Pos)])
  2968.             else
  2969.             begin
  2970.                if (VerticalScale > 9) then
  2971.                    Amp := Format(' %4.2f V',[GetAmplitude(Pos)])
  2972.                else
  2973.                    Amp := Format(' %5.3f V',[GetAmplitude(Pos)]);
  2974.             end;
  2975.          end
  2976.          else Amp := '';
  2977.          Font.Name := 'MS Sans Serif';
  2978.          Font.Size := 8;
  2979.          {$IFDEF WIN32}
  2980.          Font.Color := clInfoText;
  2981.          {$ELSE}
  2982.          Font.Color := clBlack;
  2983.          {$ENDIF}
  2984.          Text := Freq+Amp;
  2985.          aRect.Left := Pos.X;
  2986.          aRect.Top := Pos.Y+15;
  2987.          aRect.Right := aRect.Left + TextWidth(Text)+4;
  2988.          aRect.Bottom := aRect.Top + TextHeight(Text)+2;
  2989.          Border := BevelExtend;
  2990.          if FDrawFreqScale then inc(Border,SCALEWIDTH);
  2991.          if (aRect.Bottom > Height-Border) then OffsetRect(aRect,0,-40);
  2992.          Border := BevelExtend;
  2993.          if FDrawAmpScale then inc(Border,SCALEWIDTH);
  2994.          if (aRect.Right > Width-Border) then OffsetRect(aRect,Width-Border-aRect.Right,0);
  2995.          if (aRect.Top < 0) then
  2996.          begin
  2997.             aRect.Top := 0;
  2998.             aRect.Bottom := TextHeight(Text)+2;
  2999.          end;
  3000.          if (SaveDC = 0) then
  3001.          begin
  3002.             { create memory DC for save bitmap }
  3003.             SaveDC := CreateCompatibleDC(Canvas.Handle);
  3004.             { create bitmap to store background }
  3005.             SaveWidth := 10*TextWidth('W')+4;
  3006.             SaveHeight := TextHeight('W')+2;
  3007.             SaveBitmap := CreateCompatibleBitmap(Canvas.Handle,SaveWidth,SaveHeight);
  3008.             OldBitmap := SelectObject(SaveDC, SaveBitmap);
  3009.          end
  3010.          else
  3011.             { restore background }
  3012.             BitBlt(Canvas.Handle,SaveInfoPos.X,
  3013.                    SaveInfoPos.Y,SaveWidth,SaveHeight,
  3014.                    SaveDC,0,0,SRCCOPY);
  3015.          { save background }
  3016.          BitBlt(SaveDC,0,0,SaveWidth,SaveHeight,
  3017.                 Canvas.Handle,aRect.Left,aRect.Top,SRCCOPY);
  3018.          SaveInfoPos := aRect.TopLeft;
  3019.          Brush.Color := INFOCOLOR;
  3020.          Brush.Style := bsSolid;
  3021.          Pen.Color := clBlack;
  3022.          Rectangle(aRect.Left,aRect.Top,aRect.Right,aRect.Bottom);
  3023.          Brush.Style := bsClear;
  3024.          DrawText(Handle, StrPCopy(Buf, Text), -1, aRect,
  3025.                   DT_SINGLELINE  or DT_VCENTER or DT_CENTER or DT_NOPREFIX);
  3026.          Brush.Style := bsSolid;
  3027.       end
  3028.       else if (SaveDC <> 0) then
  3029.       begin
  3030.          { restore background }
  3031.          BitBlt(Canvas.Handle,SaveInfoPos.X,
  3032.                    SaveInfoPos.Y,SaveWidth,SaveHeight,
  3033.                    SaveDC,0,0,SRCCOPY);
  3034.       end;
  3035.    end;
  3036. end;
  3037. {$ENDIF}
  3038. {-- TMMSpectrum ---------------------------------------------------------}
  3039. procedure TMMSpectrum.DrawSpectrum(Clear: Boolean);
  3040. Label Calc;
  3041. begin                                             { reset the peak index }
  3042.    FPeak.Amplitude := 0;
  3043.    FPeak.Index := 0;
  3044.    DIBCanvas.DIB_InitDrawing;
  3045.    InitLocalVariables;
  3046.                                           { Clear background or draw DIB }
  3047.    if (FKind <> skScroll) or Clear then
  3048.    begin
  3049.       if not assigned(FOnDrawBar) then
  3050.       begin
  3051.          if ((FKind = skBars) or (FKind = skPeaks)) and FDrawInactive then
  3052.          begin
  3053.              DIBCanvas.DIB_CopyDIBBits(FBarDIB.Surface,0,0,FWidth,FHeight,0,0);
  3054.              goto Calc;
  3055.          end
  3056.          else if (FKind <> skScroll) then
  3057.          begin
  3058.             if assigned(FOnClearBackground) then
  3059.                FOnClearBackground(Self, DIBCanvas, Rect(0,0,FWidth,FHeight))
  3060.             else
  3061.                DrawBackGround;
  3062.             goto Calc;
  3063.          end;
  3064.       end;
  3065.       if assigned(FOnClearBackground) then
  3066.       begin
  3067.          FOnClearBackground(Self, DIBCanvas, Rect(0,0,FWidth,FHeight));
  3068.       end
  3069.       else
  3070.       begin
  3071.          DIBCanvas.DIB_SetTColor(Color);
  3072.          DIBCanvas.DIB_Clear;
  3073.       end;
  3074.    end
  3075.    else if (FKind = skScroll) and not (csDesigning in ComponentState) then
  3076.    begin                                                   { scroll down }
  3077.       DIBCanvas.DIB_CopyDIBBits(biSurface,0,SCROLLDISTANCE,FWidth,FHeight-SCROLLDISTANCE,0,0);
  3078.       DIBCanvas.DIB_SetTColor(Color);
  3079.       DIBCanvas.DIB_FillRect(Rect(0,0,FWidth,SCROLLDISTANCE));
  3080.    end;
  3081. Calc:
  3082.    CalcDisplayValues;                    { calculate the amplitude values }
  3083.    if (FKind <> skScroll) then DrawGrids;                 { draw the grid }
  3084.    case FKind of                            { draw the spectrum to bitmap }
  3085.       skDots  : DrawAsDots;
  3086.       skLines,
  3087.       skScroll: DrawAsLines;
  3088.       skVLines: DrawAsVLines;
  3089.       skBars,
  3090.       skPeaks: DrawAsBars;
  3091.    end;
  3092.                                                          { copy to screen }
  3093.    DIBCanvas.DIB_BitBlt(Canvas.Handle, FClientRect,0,0);
  3094.    DIBCanvas.DIB_DoneDrawing;
  3095.    DrawPeakValue;                                   { Draw the Peak value }
  3096. end;
  3097. {-- TMMSpectrum ---------------------------------------------------------}
  3098. Procedure TMMSpectrum.Paint;
  3099. var
  3100.    H,L: integer;
  3101.    Text: String;
  3102.    aRect: TRect;
  3103. begin
  3104.    with Canvas do
  3105.    begin
  3106.       if FDrawFreqScale or FDrawAmpScale then
  3107.       begin
  3108.          { clear the space between the scales only, to eliminate flicker }
  3109.          Brush.Color := GetScaleBackColor;
  3110.          Brush.Style := bsSolid;
  3111.          if FDrawAmpScale then
  3112.          begin
  3113.             H := Height;
  3114.             if FDrawFreqScale then H := Height-SCALEHEIGHT;
  3115.             aRect := Rect(SCALEWIDTH-3,0,SCALEWIDTH,H);
  3116.             FillRect(aRect);
  3117.             aRect:= Rect(Width-SCALEWIDTH,0,Width-SCALEWIDTH+3,H);
  3118.             FillRect(aRect);
  3119.          end;
  3120.          if FDrawFreqScale then
  3121.          begin
  3122.             aRect:= Rect(0,Height-SCALEHEIGHT,Width,Height-SCALEHEIGHT+3);
  3123.             FillRect(aRect);
  3124.             if FdrawAmpScale then
  3125.             begin
  3126.                aRect:= Rect(0,Height-SCALEHEIGHT,SCALEWIDTH,Height);
  3127.                FillRect(aRect);
  3128.                aRect:= Rect(WIDTH-SCALEWIDTH,Height-SCALEHEIGHT,Width,Height);
  3129.                FillRect(aRect);
  3130.             end;
  3131.          end;
  3132.          { write scale text }
  3133.          Canvas.Font.Color := FScaleTextColor;
  3134.          if FDrawAmpScale and FDrawFreqScale then
  3135.          begin
  3136.             if LogAmp then
  3137.             begin
  3138.                Text := 'db';
  3139.                L := SCALEWIDTH-9;
  3140.             end
  3141.             else
  3142.             begin
  3143.                Text := 'V';
  3144.                L := SCALEWIDTH-16;
  3145.             end;
  3146.             TextOutAligned(Canvas, L, Height-SCALEWIDTH,
  3147.                            Text, SCALEFONT,SCALEFONTSIZE, 1);
  3148.             TextOutAligned(Canvas, Width-SCALEWIDTH+12, Height-SCALEWIDTH,
  3149.                            Text, SCALEFONT,SCALEFONTSIZE,0);
  3150.             TextOutAligned(Canvas, Width-SCALEWIDTH+2, Height-SCALEHEIGHT+20,
  3151.                            'Hz', SCALEFONT,SCALEFONTSIZE,0);
  3152.          end;
  3153.          
  3154.          { make place for the scale }
  3155.          aRect := GetClientRect;
  3156.          if FDrawAmpScale then InflateRect(aRect,-SCALEWIDTH,0);
  3157.          if FDrawFreqScale then dec(aRect.Bottom, SCALEHEIGHT);
  3158.       end
  3159.       else aRect := GetClientRect;
  3160.       { draw the Bevel and fill the real area }
  3161.       aRect := Bevel.PaintBevel(Canvas, aRect,True);
  3162.    end;
  3163.    { now draw scales and the the spectrum }
  3164.    DrawAmplitudeScale;
  3165.    DrawFrequencyScale(True);
  3166.    DrawSpectrum(True);
  3167.    {$IFDEF BUILD_ACTIVEX}
  3168.    if Selected then
  3169.    begin
  3170.       Canvas.Brush.Style := bsClear;
  3171.       Canvas.Pen.Color   := clRed;
  3172.       Canvas.Rectangle(0,0,Width,Height);
  3173.       Canvas.Brush.Style := bsSolid;
  3174.    end;
  3175.    {$ENDIF}
  3176. end;
  3177. {-- TMMSpectrum --------------------------------------------------------}
  3178. procedure TMMSpectrum.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  3179. var
  3180.    aRect: TRect;
  3181. begin
  3182.    if not (csDesigning in ComponentState) and Enabled and (Button = mbLeft) and FShowInfo then
  3183.    begin
  3184.       aRect.TopLeft := ClientToScreen(FClientRect.TopLeft);
  3185.       aRect.BottomRight := ClientToScreen(FClientRect.BottomRight);
  3186.       ClipCursor(@aRect);
  3187.       FShowInfoHint := True;
  3188.       { maybe there is a hint, hide it }
  3189.       if ShowHint then
  3190.       begin
  3191.          FOldShowHint := ShowHint;
  3192.          ShowHint := False;
  3193.          Application.CancelHint;
  3194.          Update;
  3195.       end
  3196.       else FOldShowHint := False;
  3197.       DrawInfo(Point(X,Y));
  3198.    end;
  3199.    inherited MouseDown(Button, Shift, X, Y);
  3200. end;
  3201. {-- TMMSpectrum --------------------------------------------------------}
  3202. procedure TMMSpectrum.MouseMove(Shift: TShiftState; X, Y: Integer);
  3203. begin
  3204.    inherited MouseMove(Shift, X, Y);
  3205.    if FShowInfoHint then DrawInfo(Point(X,Y));
  3206. end;
  3207. {-- TMMSpectrum --------------------------------------------------------}
  3208. procedure TMMSpectrum.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  3209. begin
  3210.    if (Button = mbLeft) and FShowInfoHint then
  3211.    begin
  3212.       { restore background }
  3213.       if FEnabled then DrawInfo(Point(-1,-1));
  3214.       if (SaveDC <> 0) then
  3215.       begin
  3216.          SelectObject(SaveDC, OldBitmap);
  3217.          DeleteObject(SaveBitmap);
  3218.          SaveBitmap := 0;
  3219.          DeleteDC(SaveDC);
  3220.          SaveDC := 0;
  3221.       end;
  3222.       FShowInfoHint := False;
  3223.       ClipCursor(nil);
  3224.       ShowHint := FOldShowHint;
  3225.    end;
  3226.    inherited MouseUp(Button, Shift, X, Y);
  3227. end;
  3228. {-- TMMSpectrum --------------------------------------------------------}
  3229. function TMMSpectrum.GetOptimalWidth(aWidth: integer): integer;
  3230. var
  3231.    NumBars, SpotWidth: integer;
  3232. begin
  3233.    Result := aWidth;
  3234.    if (Kind = skBars) or (Kind = skPeaks) or (Kind = skVLines) then
  3235.    begin
  3236.       NumBars := (FFTLen div 2)div FrequencyScale;
  3237.       if FDrawAmpScale then
  3238.       begin
  3239.          SpotWidth := (((aWidth-2*SCALEWIDTH)-2*BevelExtend) div NumBars);
  3240.          if SpotWidth > 0 then
  3241.             Result := 2*SCALEWIDTH + SpotWidth*NumBars + 2*BevelExtend;
  3242.       end
  3243.       else
  3244.       begin
  3245.          SpotWidth := ((aWidth-2*BevelExtend) div NumBars);
  3246.          if SpotWidth > 0 then
  3247.             Result := SpotWidth*NumBars + 2*BevelExtend;
  3248.       end;
  3249.    end;
  3250. end;
  3251. end.