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

Delphi控件源码

开发平台:

Delphi

  1. {========================================================================}
  2. {=                (c) 1995-98 SwiftSoft Ronald Dittrich                 =}
  3. {========================================================================}
  4. {=                          All Rights Reserved                         =}
  5. {========================================================================}
  6. {=  D 01099 Dresden             = Tel.: +0351-8012255                   =}
  7. {=  Loewenstr.7a                = info@swiftsoft.de                     =}
  8. {========================================================================}
  9. {=  Actual versions on http://www.swiftsoft.de/mmtools.html             =}
  10. {========================================================================}
  11. {=  This code is for reference purposes only and may not be copied or   =}
  12. {=  distributed in any format electronic or otherwise except one copy   =}
  13. {=  for backup purposes.                                                =}
  14. {=                                                                      =}
  15. {=  No Delphi Component Kit or Component individually or in a collection=}
  16. {=  subclassed or otherwise from the code in this unit, or associated   =}
  17. {=  .pas, .dfm, .dcu, .asm or .obj files may be sold or distributed     =}
  18. {=  without express permission from SwiftSoft.                          =}
  19. {=                                                                      =}
  20. {=  For more licence informations please refer to the associated        =}
  21. {=  HelpFile.                                                           =}
  22. {========================================================================}
  23. {=  $Date: 03.03.98 - 18:51:13 $                                        =}
  24. {========================================================================}
  25. Unit MMLight;
  26. {$I COMPILER.INC}
  27. interface
  28. uses
  29. {$IFDEF WIN32}
  30.     Windows,
  31. {$ELSE}
  32.     WinTypes,
  33.     WinProcs,
  34. {$ENDIF}
  35.     SysUtils,
  36.     Messages,
  37.     Classes,
  38.     Graphics,
  39.     Controls,
  40.     Forms,
  41.     Menus,
  42.     MMSystem,
  43.     MMUtils,
  44.     MMObj,
  45.     MMString,
  46.     MMMath,
  47.     MMMulDiv,
  48.     MMFFT,
  49.     MMRegs,
  50.     MMPCMSup,
  51.     MMDIBCv;
  52. const
  53.     MAX_FFTLEN      = 4096; { Define the maximum FFT buffer length.        }
  54.     MAXDECAYCOUNT   = 32;   { Maximum amount of temporal averaging allowed }
  55. type
  56.     TMMLightKind    = (lkCircle,lkSphere);
  57.     TMMLightArrange = (laLine,laTriangle);
  58.     TMMLightPeakMode= (pmRMS,pmPeak,pmAverage);
  59. const
  60.     {$IFDEF CBUILDER3} {$EXTERNALSYM defRealize} {$ENDIF}
  61.     defRealize          = True;
  62.     {$IFDEF CBUILDER3} {$EXTERNALSYM defEnabled} {$ENDIF}
  63.     defEnabled          = True;
  64.     {$IFDEF CBUILDER3} {$EXTERNALSYM defHeight} {$ENDIF}
  65.     defHeight           = 90;
  66.     {$IFDEF CBUILDER3} {$EXTERNALSYM defWidth} {$ENDIF}
  67.     defWidth            = 194;
  68.     {$IFDEF CBUILDER3} {$EXTERNALSYM defMode} {$ENDIF}
  69.     defMode             = mMono;
  70.     {$IFDEF CBUILDER3} {$EXTERNALSYM defBitLength} {$ENDIF}
  71.     defBitLength        = b8bit;
  72.     {$IFDEF CBUILDER3} {$EXTERNALSYM defChannel} {$ENDIF}
  73.     defChannel          = chBoth;
  74.     {$IFDEF CBUILDER3} {$EXTERNALSYM defSampleRate} {$ENDIF}
  75.     defSampleRate       = 11025;
  76.     {$IFDEF CBUILDER3} {$EXTERNALSYM defFFTLen} {$ENDIF}
  77.     defFFTLen           = 128;
  78.     {$IFDEF CBUILDER3} {$EXTERNALSYM defWindow} {$ENDIF}
  79.     defWindow           = fwHamming;
  80.     {$IFDEF CBUILDER3} {$EXTERNALSYM defDecayMode} {$ENDIF}
  81.     defDecayMode        = dmNone;
  82.     {$IFDEF CBUILDER3} {$EXTERNALSYM defDecay} {$ENDIF}
  83.     defDecay            = 1;
  84.     {$IFDEF CBUILDER3} {$EXTERNALSYM defPeakMode} {$ENDIF}
  85.     defPeakMode         = pmPeak;
  86.     {$IFDEF CBUILDER3} {$EXTERNALSYM defKind} {$ENDIF}
  87.     defKind             = lkCircle;
  88.     {$IFDEF CBUILDER3} {$EXTERNALSYM defArrange} {$ENDIF}
  89.     defArrange          = laLine;
  90.     {$IFDEF CBUILDER3} {$EXTERNALSYM defTriangleDist} {$ENDIF}
  91.     defTriangleDist     = 10;
  92.     {$IFDEF CBUILDER3} {$EXTERNALSYM defSphereHorz} {$ENDIF}
  93.     defSphereHorz       = 1.0;
  94.     {$IFDEF CBUILDER3} {$EXTERNALSYM defSphereVert} {$ENDIF}
  95.     defSphereVert       = 1.0;
  96.     {$IFDEF CBUILDER3} {$EXTERNALSYM defZoneCount} {$ENDIF}
  97.     defZoneCount        = 60;
  98.     {$IFDEF CBUILDER3} {$EXTERNALSYM defColor} {$ENDIF}
  99.     defColor            = clBlack;
  100. type
  101.     EMMLightError   = class(Exception);
  102.     { array for uniform decay mode values }
  103.     PDataBuf        = ^TDataBuf;
  104.     TDataBuf        = array[0..MAXDECAYCOUNT-1] of PLongArray;
  105.     { struct to hold pre-calculated values for every band }
  106.     Values          = record
  107.        OldValue: Longint;
  108.        CurValue: Longint;
  109.     end;
  110.     PValues         = ^TValues;
  111.     TValues         = array[0..0] of Values;
  112.     {-- TMMLight --------------------------------------------------------}
  113.     TMMLight = class(TMMDIBGraphicControl)
  114.     private
  115.       {$IFDEF WIN32}
  116.       FpFFT           : PFFTReal;   { the instance for FFT calculation    }
  117.       {$ELSE}
  118.       FFT             : TMMFFT;     { the FFT object                      }
  119.       {$ENDIF}
  120.       FFFTData        : PSmallArray;{ Array for FFT data                  }
  121.       FWinBuf         : PIntArray;  { Array storing windowing function    }
  122.       FDataBuf        : PDataBuf;   { Memory for averaging mode           }
  123.       FDisplayVal     : PLongArray; { Array storing display values        }
  124.       FValues         : PValues;    { array with precalculted bin values  }
  125.       FLastVal_F      : PFloatArray;{ Last value buffer for exp decay mode}
  126.       FLastVal        : PLongArray; { Last value buffer for uniform avg   }
  127.       Fx1             : PIntArray;  { Array of bin #'s displayed          }
  128.       Fx2             : PIntArray;  { Array of terminal bin #'s           }
  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.       FFTLen          : integer;    { Number of points for FFT            }
  137.       FSampleRate     : Longint;    { A/D sampling rate                   }
  138.       FAmpScale       : Float;      { scaling factor for amplitude scaling}
  139.       FGainBass       : Float;      { gain factor for bass frequency light}
  140.       FGainMiddle     : Float;      { gain factor for middle freq. light  }
  141.       FGainTreble     : Float;      { gain factor for treble freq. light  }
  142.       FWindow         : TMMFFTWindow;{ selected window function           }
  143.       FEnabled        : Boolean;    { Enable or disable Light             }
  144.       FBits           : TMMBits;    { b8bit or b16bit                     }
  145.       FChannel        : TMMChannel; { chBoth, chLeft or chRigth           }
  146.       FMode           : TMMMode;    { mMono, mStereo or mQuadro           }
  147.       FBytes          : Longint;    { calculated data bytes p. Light}
  148.       FWidth          : integer;    { calculated width without border     }
  149.       FHeight         : integer;    { calculated height without border    }
  150.       FClientRect     : TRect;      { calculated beveled Rect             }
  151.       FPeakMode       : TMMLightPeakMode;
  152.       FKind           : TMMLightKind;
  153.       FArrange        : TMMLightArrange;
  154.       FTriangleDist   : Integer;
  155.       FSphereHorz     : Float;
  156.       FSphereVert     : Float;
  157.       FZoneCount      : Integer;
  158.       { Events }
  159.       FOnPcmOverflow  : TNotifyEvent;
  160.       procedure CreateDataBuffers(Length: Cardinal);
  161.       procedure FreeDataBuffers;
  162.       procedure CreateArrays(Size: Cardinal);
  163.       procedure FreeArrays;
  164.       procedure ResetDecayBuffers;
  165.       procedure ResetValues;
  166.       procedure InitializeData;
  167.       procedure SetBytesPerLight;
  168.       procedure SetupScale;
  169.       procedure CalcMagnitude(MagnitudeForm: Boolean);
  170.       procedure CalcDisplayValues;
  171.       procedure DrawLight;
  172.       procedure AdjustCtrlSize(var W, H: Integer);
  173.       procedure SetFFTLen(aLength: integer);
  174.       procedure SetDecayMode(aValue: TMMDecayMode);
  175.       procedure SetDecay(aValue: integer);
  176.       procedure SetWindow(aValue: TMMFFTWindow);
  177.       procedure SetAmpScale(index: integer; aValue: integer);
  178.       function  GetAmpScale(index: integer): integer;
  179.       procedure SetEnabled(aValue: Boolean);
  180.       procedure SetPCMWaveFormat(wf: TPCMWaveFormat);
  181.       function  GetPCMWaveFormat: TPCMWaveFormat;
  182.       procedure SetBits(aValue: TMMBits);
  183.       procedure SetChannel(aValue: TMMChannel);
  184.       procedure SetMode(aValue: TMMMode);
  185.       procedure SetSampleRate(aValue: Longint);
  186.       procedure SetPeakMode(aValue: TMMLightPeakMode);
  187.       procedure SetKind(aValue: TMMLightKind);
  188.       procedure SetArrange(aValue: TMMLightArrange);
  189.       procedure SetTriangleDist(Value: Integer);
  190.       procedure SetSphereHorz(Value: Float);
  191.       procedure SetSphereVert(Value: Float);
  192.       procedure SetZoneCount(Value: Integer);
  193.     protected
  194.       procedure Paint; override;
  195.       procedure Loaded; override;
  196.       procedure PcmOverflow; dynamic;
  197.       procedure Changed; override;
  198.       procedure InitDIB;
  199.       procedure DrawInitData;
  200.       procedure DrawCurrentData;
  201.       function  GetPalette: HPALETTE; override;
  202.     public
  203.       constructor Create(AOwner: TComponent); override;
  204.       destructor  Destroy; override;
  205.       procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); override;
  206.       procedure RefreshPCMData(PCMData: Pointer);
  207.       procedure RefreshFFTData(FFTData: Pointer);
  208.       procedure RefreshMagnitudeData(MagData: Pointer);
  209.       procedure ResetData;
  210.       property  BytesPerLight: Longint read FBytes;
  211.       property  PCMWaveFormat: TPCMWaveFormat read GetPCMWaveFormat write SetPCMWaveFormat;
  212.     published
  213.       { Events }
  214.       property OnClick;
  215.       property OnDblClick;
  216.       property OnMouseDown;
  217.       property OnMouseMove;
  218.       property OnMouseUp;
  219.       property OnDragDrop;
  220.       property OnDragOver;
  221.       property OnEndDrag;
  222.       property OnStartDrag;
  223.       property OnPcmOverflow: TNotifyEvent read FOnPcmOverflow write FOnPcmOverflow;
  224.       property Align;
  225.       property Bevel;
  226.       property Color default defColor;
  227.       property ParentShowHint;
  228.       property ShowHint;
  229.       property Visible;
  230.       property PopupMenu;
  231.       property PaletteRealize default defRealize;
  232.       property PaletteMapped;
  233.       property Enabled: Boolean read FEnabled write SetEnabled default defEnabled;
  234.       property Height default defHeight;
  235.       property Width default defWidth;
  236.       property Mode: TMMMode read FMode write SetMode default defMode;
  237.       property BitLength: TMMBits read FBits write SetBits default defBitLength;
  238.       property Channel: TMMChannel read FChannel write SetChannel default defChannel;
  239.       property SampleRate: Longint read FSampleRate write SetSampleRate default defSampleRate;
  240.       property FFTLength: integer read FFTLen write SetFFTLen default defFFTLen;
  241.       property Window: TMMFFTWindow read FWindow write SetWindow default defWindow;
  242.       property DecayMode: TMMDecayMode read FDecayMode write SetDecayMode default defDecayMode;
  243.       property Decay: integer read FDecay write SetDecay default defDecay;
  244.       property AmplitudeScale: integer index 0 read GetAmpScale write SetAmpScale;
  245.       property GainBass: integer index 1 read GetAmpScale write SetAmpScale;
  246.       property GainMiddle: integer index 2 read GetAmpScale write SetAmpScale;
  247.       property GainTreble: integer index 3 read GetAmpScale write SetAmpScale;
  248.       property PeakMode: TMMLightPeakMode read FPeakMode write SetPeakMode default defPeakMode;
  249.       property Kind: TMMLightKind read FKind write SetKind default defKind;
  250.       property Arrange: TMMLightArrange read FArrange write SetArrange default defArrange;
  251.       property TriangleDist: Integer read FTriangleDist write SetTriangleDist default defTriangleDist;
  252.       property SphereHorz: Float read FSphereHorz write SetSphereHorz;
  253.       property SphereVert: Float read FSphereVert write SetSphereVert;
  254.       property ZoneCount: Integer read FZoneCount write SetZoneCount default defZoneCount;
  255.     end;
  256. implementation
  257. uses
  258.     Consts;
  259. const
  260.      NumLights = 3;
  261.      { Here we have the Center Frequencys from the different bands }
  262.      CenterFreq: array[0..NumLights-1] of integer = (150,750,1750);
  263. {-- TMMLight ------------------------------------------------------------}
  264. constructor TMMLight.Create(AOwner: TComponent);
  265. begin
  266.    inherited Create(AOwner);
  267.    CreateDataBuffers(MAX_FFTLEN);
  268.    CreateArrays(NumLights);
  269.    PaletteRealize := defRealize;
  270.    {$IFDEF WIN32}
  271.    FpFFT := InitRealFFT(8);
  272.    {$ELSE}
  273.    FFT := TMMFFT.Create;
  274.    {$ENDIF}
  275.    FFTLen := 8;
  276.    FDecay := defDecay;
  277.    FDecayMode := defDecayMode;
  278.    FDecayFactor := 0.0001;
  279.    FDecayCount := 1;
  280.    FDecayCntAct := 0;
  281.    FDecayPtr := 0;
  282.    FSampleRate := defSampleRate;
  283.    FChannel := defChannel;
  284.    FBits := defBitLength;
  285.    FMode := defMode;
  286.    FWindow := defWindow;
  287.    FAmpScale := 1.0;
  288.    FGainBass := 0.05;
  289.    FGainMiddle := 0.05;
  290.    FGainTreble := 0.05;
  291.    FEnabled := defEnabled;
  292.    FPeakMode := defPeakMode;
  293.    FKind := defKind;
  294.    FArrange := defArrange;
  295.    FTriangleDist := defTriangleDist;
  296.    FSphereHorz := defSphereHorz;
  297.    FSphereVert := defSphereVert;
  298.    FZoneCount := defZoneCount;
  299.    FFTLength := defFFTLen;
  300.    Color := defColor;
  301.    Height := defHeight;
  302.    Width := defWidth;
  303.    InitDIB;
  304.    ErrorCode := ComponentRegistered(InitCode, Self, ClassName);
  305.    if (ErrorCode <> 0) then RegisterFailed(InitCode, Self , ClassName);
  306. end;
  307. {-- TMMLight ------------------------------------------------------------}
  308. Destructor TMMLight.Destroy;
  309. begin
  310.    FreeDataBuffers;
  311.    FreeArrays;
  312.    {$IFDEF WIN32}
  313.    DoneRealFFT(FpFFT);
  314.    {$ELSE}
  315.    FFT.Free;
  316.    {$ENDIF}
  317.    inherited Destroy;
  318. end;
  319. {-- TMMLight ------------------------------------------------------------}
  320. procedure TMMLight.PcmOverflow;
  321. begin
  322.    if Assigned(FOnPcmOverflow) then FOnPcmOverflow(Self);
  323. end;
  324. {-- TMMLight ------------------------------------------------------------}
  325. procedure TMMLight.CreateDataBuffers(Length: Cardinal);
  326. begin
  327.    if (Length > 0) then
  328.    begin
  329.       FFFTData   := GlobalAllocMem(Length * sizeOf(SmallInt));
  330.       FWinBuf    := GlobalAllocMem(Length * sizeOf(Integer));
  331.       FDisplayVal:= GlobalAllocMem((Length div 2) * sizeOf(Long));
  332.       FLastVal   := GlobalAllocMem((Length div 2) * sizeOf(Long));
  333.       FLastVal_F := GlobalAllocMem((Length div 2) * sizeOf(Float));
  334.       FDataBuf   := GlobalAllocMem(MAXDECAYCOUNT * sizeOf(PLongArray));
  335.       {$IFDEF WIN32}
  336.       {$IFDEF TRIAL}
  337.       {$DEFINE _HACK1}
  338.       {$I MMHACK.INC}
  339.       {$ENDIF}
  340.       {$ENDIF}
  341.       FMaxDecayCount := 0;
  342.       while FMaxDecayCount < MAXDECAYCOUNT do
  343.       begin
  344.          FDataBuf^[FMaxDecayCount] := GlobalAllocMem((Length div 2) * sizeOf(Long));
  345.          if FDataBuf^[FMaxDecayCount] = nil then break;
  346.          inc(FMaxDecayCount);
  347.       end;
  348.       if (FMaxDecayCount < 1) then OutOfMemoryError;
  349.       FDecayCount := Min(FDecayCount, FMaxDecayCount);
  350.       { Clear out the memory buffers }
  351.       ResetDecayBuffers;
  352.    end;
  353. end;
  354. {-- TMMLight ------------------------------------------------------------}
  355. procedure TMMLight.FreeDataBuffers;
  356. var
  357.    i: integer;
  358. begin
  359.    GlobalFreeMem(Pointer(FFFTData));
  360.    GlobalFreeMem(Pointer(FWinBuf));
  361.    GlobalFreeMem(Pointer(FDisplayVal));
  362.    GlobalFreeMem(Pointer(FLastVal));
  363.    GlobalFreeMem(Pointer(FLastVal_F));
  364.    if FDataBuf <> nil then
  365.    begin
  366.       for i := 0 to FMaxDecayCount-1 do
  367.           if FDataBuf^[i] <> nil then GlobalFreeMem(Pointer(FDataBuf^[i]));
  368.       GlobalFreeMem(Pointer(FDataBuf));
  369.    end;
  370. end;
  371. {-- TMMLight ------------------------------------------------------------}
  372. procedure TMMLight.CreateArrays(Size: Cardinal);
  373. begin
  374.    if (Size > 0) then
  375.    begin
  376.       Fx1     := GlobalAllocMem(Size * sizeOf(Integer));
  377.       Fx2     := GlobalAllocMem(Size * sizeOf(Integer));
  378.       FValues := GlobalAllocMem(Size * sizeOf(TValues));
  379.    end;
  380. end;
  381. {-- TMMLight ------------------------------------------------------------}
  382. procedure TMMLight.FreeArrays;
  383. begin
  384.    GlobalFreeMem(Pointer(Fx1));
  385.    GlobalFreeMem(Pointer(Fx2));
  386.    GlobalFreeMem(Pointer(FValues));
  387. end;
  388. {-- TMMLight ------------------------------------------------------------}
  389. procedure TMMLight.ResetDecayBuffers;
  390. var
  391.    i, j: integer;
  392. begin
  393.    FDecayPtr := 0;
  394.    FDecayCntAct := 0; { Restart the count of number of samples taken }
  395.    FillChar(FLastVal^, (FFTLen div 2)*sizeOf(Long),0);
  396.    FillChar(FLastVal_F^, (FFTLen div 2)*sizeOf(Float),0);
  397.    for i := 0 to FMaxDecayCount-1 do
  398.        for j := 0 to (FFTLen div 2)-1 do FDataBuf^[i]^[j] := 0;
  399. end;
  400. {-- TMMLight ------------------------------------------------------------}
  401. procedure TMMLight.ResetValues;
  402. var
  403.    i: integer;
  404. begin
  405.    for i := 0 to NumLights-1 do
  406.    begin
  407.       FValues^[i].OldValue := -1;
  408.       FValues^[i].CurValue := 0;
  409.    end;
  410. end;
  411. {-- TMMLight ------------------------------------------------------------}
  412. procedure TMMLight.InitializeData;
  413. Var
  414.    i: integer;
  415. begin
  416.    if Enabled and (csDesigning in ComponentState) then
  417.    begin
  418.       Randomize;
  419.       for i := 0 to FFTLen div 2-1 do
  420.       begin                                         { create random data }
  421.          FDisplayVal^[i] := Long(Random(32767));
  422.       end;
  423.       ResetValues;
  424.    end
  425.    else
  426.    begin                                              { create zero data }
  427.       FillChar(FDisplayVal^[0], FFTLen div 2 * sizeOf(Long), 0);
  428.       FillChar(FFFTData^[0], FFTLen * sizeOf(SmallInt), 0);
  429.       ResetDecayBuffers;
  430.       ResetValues;
  431.    end;
  432. end;
  433. {-- TMMLight ------------------------------------------------------------}
  434. procedure TMMLight.ResetData;
  435. begin
  436.    InitializeData;
  437.    Refresh;
  438. end;
  439. {-- TMMLight ------------------------------------------------------------}
  440. procedure TMMLight.SetFFTLen(aLength: integer);
  441. var
  442.    Order: integer;
  443. begin
  444.    aLength := MinMax(aLength,8,MAX_FFTLEN);
  445.    { Convert FFTLen to a power of 2 }
  446.    Order := 0;
  447.    while aLength > 1 do
  448.    begin
  449.       aLength := aLength shr 1;
  450.       inc(Order);
  451.    end;
  452.    if (Order > 0) then aLength := aLength shl Order;
  453.    {$IFDEF WIN32}
  454.    {$IFDEF TRIAL}
  455.    {$DEFINE _HACK2}
  456.    {$I MMHACK.INC}
  457.    {$ENDIF}
  458.    {$ENDIF}
  459.    if (aLength <> FFTLen) then
  460.    begin
  461.       { re-init the FFTObject with the new FFT-length }
  462.       {$IFDEF WIN32}
  463.       DoneRealFFT(FpFFT);
  464.       FpFFT := InitRealFFT(Order);
  465.       FFTLen := aLength;
  466.       GenWindowTableInt(FWinBuf,Ord(FWindow),Trunc(Log2(FFTLen)));
  467.       {$ELSE}
  468.       FFT.FFTLength := aLength;
  469.       FFTLen := aLength;
  470.       GenWindowTableInt(FWinBuf,Ord(FWindow),Trunc(Log2(FFTLen)));
  471.       {$ENDIF}
  472.       { Re-initialize the display }
  473.       SetupScale;
  474.       SetBytesPerLight;
  475.       InitDIB;
  476.    end;
  477. end;
  478. {-- TMMLight ------------------------------------------------------------}
  479. procedure TMMLight.SetDecayMode(aValue: TMMDecayMode);
  480. begin
  481.    { Select averaging mode }
  482.    if (aValue <> FDecayMode) then
  483.    begin
  484.       FDecayMode := aValue;
  485.       { Re-initialize the buffers }
  486.       ResetDecayBuffers;
  487.    end;
  488.    {$IFDEF WIN32}
  489.    {$IFDEF TRIAL}
  490.    {$DEFINE _HACK2}
  491.    {$I MMHACK.INC}
  492.    {$ENDIF}
  493.    {$ENDIF}
  494. end;
  495. {-- TMMLight ------------------------------------------------------------}
  496. procedure TMMLight.SetDecay(aValue: integer);
  497. var
  498.    i: integer;
  499. begin
  500.    aValue := MinMax(aValue,1,16);
  501.    if (aValue <> FDecay) then
  502.    begin
  503.       FDecay := aValue;
  504.       { factor for stepUp and exponential averaging }
  505.       FDecayFactor := 0.0001;
  506.       for i := 0 to FDecay-1 do
  507.           FDecayFactor := sqrt(FDecayFactor);
  508.       { counter for uniform averaging }
  509.       FDecayCount := MinMax(2*(aValue-1),1,MaxDecayCount);
  510.       { Re-initialize the buffers for uniform averaging }
  511.       if (FDecayMode = dmUniform) then ResetDecayBuffers;
  512.    end;
  513. end;
  514. {-- TMMLight ------------------------------------------------------------}
  515. procedure TMMLight.SetWindow(aValue: TMMFFTWindow);
  516. begin
  517.    if (aValue <> FWindow) then
  518.    begin
  519.       FWindow := aValue;
  520.       GenWindowTableInt(FWinBuf,Ord(FWindow),Trunc(Log2(FFTLen)));
  521.    end;
  522. end;
  523. {-- TMMLight ------------------------------------------------------------}
  524. procedure TMMLight.SetSampleRate(aValue: Longint);
  525. begin
  526.    if (aValue <> FSampleRate) then
  527.    begin
  528.       FSampleRate := MinMax(aValue, 8000, 100000);
  529.       { Re-initialize the display }
  530.       SetupScale;
  531.       InitDIB;
  532.    end;
  533. end;
  534. {-- TMMLight ------------------------------------------------------------}
  535. procedure TMMLight.SetEnabled(aValue: Boolean);
  536. begin
  537.    if (aValue <> FEnabled) then
  538.    begin
  539.       FEnabled := aValue;
  540.       { inherited Enabled := Value }
  541.       InitDIB;
  542.    end;
  543. end;
  544. {-- TMMLight ------------------------------------------------------------}
  545. procedure TMMLight.SetKind(aValue: TMMLightKind);
  546. begin
  547.     if (aValue <> FKind) then
  548.     begin
  549.        FKind := aValue;
  550.        InitDIB;
  551.     end;
  552. end;
  553. {-- TMMLight ------------------------------------------------------------}
  554. procedure TMMLight.SetArrange(aValue: TMMLightArrange);
  555. begin
  556.    if (aValue <> FArrange) then
  557.    begin
  558.       FArrange := aValue;
  559.       InitDIB;
  560.    end;
  561. end;
  562. {-- TMMLight ------------------------------------------------------------}
  563. procedure TMMLight.SetTriangleDist(Value: Integer);
  564. begin
  565.    Value := MinMax(Value,2,MaxInt);
  566.    if (Value <> FTriangleDist) then
  567.    begin
  568.       FTriangleDist := Value;
  569.       InitDIB;
  570.    end;
  571. end;
  572. {-- TMMLight ------------------------------------------------------------}
  573. procedure TMMLight.SetSphereHorz(Value: Float);
  574. begin
  575.    Value := MaxR(Value,0);
  576.    if (Value <> FSphereHorz) then
  577.    begin
  578.       FSphereHorz := Value;
  579.       InitDIB;
  580.    end;
  581. end;
  582. {-- TMMLight ------------------------------------------------------------}
  583. procedure TMMLight.SetSphereVert(Value: Float);
  584. begin
  585.    Value := MaxR(Value,0);
  586.    if (Value <> FSphereVert) then
  587.    begin
  588.       FSphereVert := Value;
  589.       InitDIB;
  590.    end;
  591. end;
  592. {-- TMMLight ------------------------------------------------------------}
  593. procedure TMMLight.SetZoneCount(Value: Integer);
  594. begin
  595.    Value := MinMax(Value,1,MaxInt);
  596.    if (Value <> FZoneCount) then
  597.    begin
  598.       FZoneCount := Value;
  599.       InitDIB;
  600.    end;
  601. end;
  602. {-- TMMLight ------------------------------------------------------------}
  603. procedure TMMLight.SetPeakMode(aValue: TMMLightPeakMode);
  604. begin
  605.    if (aValue <> FPeakMode) then
  606.    begin
  607.       FPeakMode := aValue;
  608.       Refresh;
  609.    end;
  610. end;
  611. {-- TMMLight ------------------------------------------------------------}
  612. procedure TMMLight.Loaded;
  613. begin
  614.    inherited Loaded;
  615.    SetupScale;
  616.    InitDIB;
  617. end;
  618. {-- TMMLight ------------------------------------------------------------}
  619. procedure TMMLight.AdjustCtrlSize(var W, H: Integer);
  620. begin
  621.    W := Max(W,2*BevelExtend+5);
  622.    H := Max(H,2*BevelExtend+5);
  623. end;
  624. {-- TMMLight ------------------------------------------------------------}
  625. procedure TMMLight.SetBounds(aLeft, aTop, aWidth, aHeight: integer);
  626. var
  627.   W, H: Integer;
  628. begin
  629.    W := aWidth;
  630.    H := aHeight;
  631.    AdjustCtrlSize (W, H);
  632.    inherited SetBounds(aLeft, aTop, W, H);
  633.    Changed;
  634. end;
  635. {-- TMMLight ------------------------------------------------------------}
  636. procedure TMMLight.Changed;
  637. begin
  638.    FClientRect := BeveledRect;
  639.    { save the real height and width }
  640.    FWidth  := Max(FClientRect.Right - FClientRect.Left,4);
  641.    FHeight := Max(FClientRect.Bottom - FClientRect.Top,4);
  642.    DIBCanvas.SetBounds(0,0,FWidth,FHeight);
  643.    InitDIB;
  644.    inherited Changed;
  645. end;
  646. {-- TMMLight ------------------------------------------------------------}
  647. procedure TMMLight.SetBytesPerLight;
  648. begin
  649.    FBytes := (Ord(FBits)+1) * (Ord(FMode)+1) * FFTLen;
  650. end;
  651. {-- TMMLight ------------------------------------------------------------}
  652. Procedure TMMLight.SetPCMWaveFormat(wf: TPCMWaveFormat);
  653. var
  654.    pwfx: PWaveFormatEx;
  655. begin
  656.    pwfx := @wf;
  657.    if not pcmIsValidFormat(pwfx) then
  658.       raise EMMLightError.Create(LoadResStr(IDS_INVALIDFORMAT));
  659.    SampleRate := pwfx^.nSamplesPerSec;
  660.    BitLength := TMMBits(pwfx^.wBitsPerSample div 8 - 1);
  661.    Mode := TMMMode(pwfx^.nChannels-1);
  662. end;
  663. {-- TMMLight ------------------------------------------------------------}
  664. function TMMLight.GetPCMWaveFormat: TPCMWaveFormat;
  665. var
  666.    wfx: TWaveFormatEx;
  667. begin
  668.    pcmBuildWaveHeader(@wfx, (Ord(FBits)+1)*8, Ord(FMode)+1, SampleRate);
  669.    Result := PPCMWaveFormat(@wfx)^;
  670. end;
  671. {-- TMMLight ------------------------------------------------------------}
  672. Procedure TMMLight.SetBits(aValue: TMMBits);
  673. begin
  674.    if (aValue <> FBits) then
  675.    begin
  676.       FBits := aValue;
  677.       SetBytesPerLight;
  678.    end;
  679. end;
  680. {-- TMMLight ------------------------------------------------------------}
  681. Procedure TMMLight.SetChannel(aValue: TMMChannel);
  682. begin
  683.    if (aValue <> FChannel) then
  684.    begin
  685.       FChannel := aValue;
  686.       SetBytesPerLight;
  687.    end;
  688. end;
  689. {-- TMMLight ------------------------------------------------------------}
  690. Procedure TMMLight.SetMode(aValue: TMMMode);
  691. begin
  692.    if (aValue <> FMode) then
  693.    begin
  694.       FMode := aValue;
  695.       SetBytesPerLight;
  696.    end;
  697. end;
  698. {-- TMMLight ------------------------------------------------------------}
  699. procedure TMMLight.SetAmpScale(index: integer; aValue: integer);
  700. begin
  701.    { Change the amplitude scale factor }
  702.    aValue := MinMax(aValue, 0, 1000);
  703.    if (aValue = GetAmpScale(index)) then exit;
  704.    case index of
  705.       0: FAmpScale  := 0.01*aValue;
  706.       1: FGainBass  := 0.0005*aValue;
  707.       2: FGainMiddle:= 0.0005*aValue;
  708.       3: FGainTreble:= 0.0005*aValue;
  709.    end;
  710.    { Flush the buffers }
  711.    InitializeData;
  712. end;
  713. {-- TMMLight ------------------------------------------------------------}
  714. function TMMLight.GetAmpScale(index: integer): integer;
  715. begin
  716.    case index of
  717.       0: Result := Round(FAmpScale/0.01);
  718.       1: Result := Round(FGainBass/0.0005);
  719.       2: Result := Round(FGainMiddle/0.0005);
  720.       3: Result := Round(FGainTreble/0.0005);
  721.    else
  722.       Result := 0;
  723.    end;
  724. end;
  725. {-- TMMLight ------------------------------------------------------------}
  726. procedure TMMLight.SetupScale;
  727. var
  728.    i,ival: Longint;
  729.    StartFreq: array[0..NumLights-1] of Float;
  730. begin
  731.    if not (csLoading in ComponentState) then
  732.    begin
  733.       { Do RMS averaging into a fixed set of bins }
  734.       StartFreq[0] := 0;
  735.       for i := 1 to NumLights-1 do
  736.     StartFreq[i] := sqrt(Longint(CenterFreq[i])*CenterFreq[i-1]);
  737.       i := 0;
  738.       while i < NumLights do
  739.       begin
  740.       ival := MinMax(Round(StartFreq[i]/FSampleRate*FFTLen),0,FFTLen div 2);
  741.         Fx1^[i] := ival;
  742.         if (i > 0) then Fx2^[i-1] := ival;
  743.         inc(i);
  744.       end;
  745.       Fx2^[i-1] := FFTlen div 2-1;
  746.       { Compute the ending locations for lines holding multiple bins }
  747.       for i := 0 to NumLights-1 do
  748.           if (Fx2^[i] <= (Fx1^[i]+1)) then Fx2^[i] := 0;
  749.       { if lines are repeated on the screen, flag this so that we don't
  750.         have to recompute the y values. }
  751.       for i := NumLights-1 downTo 1 do
  752.       begin
  753.          if (Fx1^[i] = Fx1^[i-1]) then
  754.          begin
  755.            Fx1^[i] := -1;
  756.             Fx2^[i]:= 0;
  757.          end;
  758.       end;
  759.    end;
  760.    {$IFDEF WIN32}
  761.    {$IFDEF TRIAL}
  762.    {$DEFINE _HACK1}
  763.    {$I MMHACK.INC}
  764.    {$ENDIF}
  765.    {$ENDIF}
  766. end;
  767. {-- TMMLight ------------------------------------------------------------}
  768. procedure TMMLight.RefreshPCMData(PCMData: Pointer);
  769. var
  770.    Value: Longint;
  771.    i: Integer;
  772.    ReIndex: integer;
  773.    {$IFDEF WIN32}
  774.    fTemp: array[0..MAX_FFTLEN-1] of Float;
  775.    {$ELSE}
  776.    fTemp: array[0..MAX_FFTLEN-1] of Smallint;
  777.    {$ENDIF}
  778. begin
  779.    if FEnabled and Visible then
  780.    begin
  781.       ReIndex := Ord(FChannel)-1;
  782.       { perform windowing on sample Data from PCMData to FFFTData }
  783.       if (FBits = b8bit) then
  784.       begin
  785.          if (FMode = mMono) then
  786.          for i := 0 to FFTLen-1 do
  787.          begin
  788.             Value := PByteArray(PCMData)^[i];
  789.             if Value >= 255 then PcmOverflow;
  790.             fTemp[i] := MulDiv32(Value-128,FWinBuf^[i],128);
  791.          end
  792.          else if (FChannel = chBoth) then
  793.          for i := 0 to FFTLen-1 do
  794.          begin
  795.             Value := (Word(PByteArray(PCMData)^[i+i])+PByteArray(PCMData)^[i+i+1])div 2;
  796.             if Value >= 255 then PcmOverflow;
  797.             fTemp[i] := MulDiv32(Value-128,FWinBuf^[i],128);
  798.          end
  799.          else
  800.          for i := 0 to FFTLen-1 do
  801.          begin
  802.             Value := PByteArray(PCMData)^[i+i+ReIndex];
  803.             if Value >= 255 then PcmOverflow;
  804.             fTemp[i] := MulDiv32(Value-128,FWinBuf^[i],128);
  805.          end;
  806.       end
  807.       else
  808.       begin
  809.          if (FMode = mMono) then
  810.          for i := 0 to FFTLen-1 do
  811.          begin
  812.             Value := PSmallArray(PCMData)^[i];
  813.             if Value >= 32767 then PcmOverflow;
  814.             fTemp[i] := MulDiv32(Value,FWinBuf^[i],32768);
  815.          end
  816.          else if (FChannel = chBoth) then
  817.          for i := 0 to FFTLen-1 do
  818.          begin
  819.             Value := (Long(PSmallArray(PCMData)^[i+i])+PSmallArray(PCMData)^[i+i+1])div 2;
  820.             if Value >= 32766 then PcmOverflow;
  821.             fTemp[i] := MulDiv32(Value,FWinBuf^[i],32768);
  822.          end
  823.          else
  824.          for i := 0 to FFTLen-1 do
  825.          begin
  826.             Value := PSmallArray(PCMData)^[i+i+ReIndex];
  827.             if Value >= 32767 then PcmOverflow;
  828.             fTemp[i] := MulDiv32(Value,FWinBuf^[i],32768);
  829.          end;
  830.       end;
  831.       { calc the FFT }
  832.       {$IFDEF WIN32}
  833.       DoRealFFT(FpFFT,@fTemp, 1);
  834.       for i := 0 to FFTLen-1 do FFFTData^[i] := Trunc(fTemp[i]/(FFTLen div 2));
  835.       {$ELSE}
  836.       for i := 0 to FFTLen-1 do FFFTData^[i] := fTemp[i];
  837.       FFT.CalcFFT(Pointer(FFFTData));
  838.       {$ENDIF}
  839.       { calc the magnitude }
  840.       CalcMagnitude(False);
  841.       { next, put this data up on the display }
  842.       DrawLight;
  843.    end;
  844. end;
  845. {-- TMMLight ------------------------------------------------------------}
  846. procedure TMMLight.RefreshFFTData(FFTData: Pointer);
  847. begin
  848.    Move(PByte(FFTData)^, FFFTData^, FFTLen*sizeOf(SmallInt));
  849.    { calc the magnitude }
  850.    CalcMagnitude(False);
  851.    { next, put this data up on the display }
  852.    DrawLight;
  853. end;
  854. {-- TMMLight ------------------------------------------------------------}
  855. procedure TMMLight.RefreshMagnitudeData(MagData: Pointer);
  856. begin
  857.    Move(PByte(MagData)^, FFFTData^, FFTLen*sizeOf(SmallInt));
  858.    { calc display values }
  859.    CalcMagnitude(True);
  860.    { next, put this data up on the display }
  861.    DrawLight;
  862. end;
  863. {-- TMMLight ------------------------------------------------------------}
  864. procedure TMMLight.CalcMagnitude(MagnitudeForm: Boolean);
  865. var
  866.    i: integer;
  867.    re,im: Long;
  868.    a2,Root: Long;{ Variables for computing Sqrt/Log of Amplitude^2 }
  869. begin
  870.    { go through the data set and convert it to magnitude form }
  871.    inc(FDecayPtr);
  872.    inc(FDecayCntAct);
  873.    if (FDecayPtr >= FDecayCount) then FDecayPtr := 0;
  874.    if (FDecayCntAct > FDecayCount) then FDecayCntAct := FDecayCount;
  875.    for i := 0 to (FFTLen div 2)-1 do
  876.    begin
  877.       if MagnitudeForm then
  878.       begin
  879.          a2 := PLongArray(FFFTData)^[i];
  880.       end
  881.       else
  882.       begin
  883.          { Compute the magnitude }
  884.          {$IFDEF WIN32}
  885.          re := FFFTData^[i+i];
  886.          im := FFFTData^[i+i+1];
  887.          {$ELSE}
  888.          re := FFFTData^[FFT.BitReversed^[i]];
  889.          im := FFFTData^[FFT.BitReversed^[i]+1];
  890.          {$ENDIF}
  891.          a2 := re*re+im*im;
  892.       end;
  893.       { Watch for possible overflow }
  894.       if (a2 < 0) then a2 := 0;
  895.       Root := Trunc(FAmpScale*sqrt(a2));
  896.       { In decay mode, need to average this value }
  897.       case Ord(FDecayMode) of
  898.          1: begin
  899.                FLastVal_F^[i] := FLastVal_F^[i]*FDecayFactor;
  900.                if (Root >= FLastVal_F^[i]) then FLastVal_F^[i] := Root
  901.                else Root := Trunc(FLastVal_F^[i]);
  902.             end;
  903.          2: begin
  904.        FLastVal_F^[i] := FLastVal_F^[i]*FDecayFactor+(1-FDecayFactor)*Root;
  905.        Root := Floor(FLastVal_F^[i]);
  906.             end;
  907.          3: begin
  908.                FLastVal^[i] := FLastVal^[i] + (Root-FDataBuf^[FDecayPtr]^[i]);
  909.                FDataBuf^[FDecayPtr]^[i] := Root;
  910.                Root := FLastVal^[i] div FDecayCntAct;
  911.             end;
  912.       end;
  913.       FDisplayVal^[i] := Root;
  914.    end;
  915. end;
  916. {-- TMMLight ------------------------------------------------------------}
  917. procedure TMMLight.CalcDisplayValues;
  918. var
  919.    i, j, k, index: integer;
  920.    dv,val: Longint;
  921.    valf: Float;
  922. begin
  923.    dv := 0;
  924.    j := 0;
  925.    i := 0;
  926.    while i < NumLights do
  927.    begin
  928.       { If this line is the same as the previous one, just use the previous
  929.         Y value. Else go ahead and compute the value. }
  930.       index := Fx1^[i];
  931.       if (index >= 0) then
  932.       begin
  933.          if i > 0 then
  934.          begin
  935.             FValues^[j].CurValue := dv;
  936.             { now the next }
  937.             inc(j);
  938.          end;
  939.          k := 1;
  940.          dv := FDisplayVal^[index];
  941.          valf := dv;
  942.          if (Fx2^[i] > 0) then
  943.          begin
  944.             while (index < Fx2^[i]) do
  945.             begin
  946.                { We have three ways here }
  947.                case FPeakMode of
  948.                  { build the RMS value of the set of bins }
  949.                  pmRMS:
  950.                  begin
  951.                     val := FDisplayVal^[index];
  952.                     valf := valf + (val+0.1)*val;
  953.                  end;
  954.                  { search the higest bin }
  955.                  pmPeak:
  956.                  begin
  957.                     if FDisplayVal^[index] > dv then
  958.                        dv := FDisplayVal^[index];
  959.                  end;
  960.                  { average the bins }
  961.                  pmAverage:
  962.                  begin
  963.                     dv := dv + FDisplayVal^[index];
  964.                     inc(k);
  965.                  end;
  966.                end;
  967.                inc(index);
  968.             end;
  969.             case FPeakMode of
  970.               pmRMS    : dv := Trunc(sqrt(valf/Max(index-Fx1^[i],1)));
  971.               pmPeak   :;
  972.               pmAverage: dv := dv div k;
  973.             end;
  974.          end;
  975.       end;
  976.       inc(i);
  977.    end;
  978.    { store the last value }
  979.    FValues^[j].CurValue := dv;
  980. end;
  981. {-- TMMLight ------------------------------------------------------------}
  982. procedure TMMLight.InitDIB;
  983. begin
  984.    if (csLoading in ComponentState) then Exit;
  985.    if Kind = lkCircle then
  986.       DIBCanvas.AnimatedColorCount := NumLights
  987.    else
  988.       DIBCanvas.AnimatedColorCount := NumLights * ZoneCount;
  989.    DIBCanvas.DIB_InitDrawing;
  990.                                                       { clear background }
  991.    DIBCanvas.DIB_SetTColor(Color);
  992.    DIBCanvas.DIB_Clear;
  993.    { Flush the buffers }
  994.    InitializeData;
  995.    DrawInitData;
  996.    DIBCanvas.DIB_DoneDrawing;
  997.    Invalidate;
  998. end;
  999. {-- TMMLight ------------------------------------------------------------}
  1000. procedure TMMLight.DrawInitData;
  1001. var
  1002.     i       : Integer;
  1003.     j       : Integer;
  1004.     AWidth  : Integer;
  1005.     AHeight : Integer;
  1006.     ERect   : TRect;
  1007.     R       : TRect;
  1008.     Delta   : Integer;
  1009.     Radius  : Integer;
  1010.     Vert    : Boolean;
  1011.     procedure DrawCircle(X,Y,W,H: Integer; Color: Integer);
  1012.     begin
  1013.        with DIBCanvas do
  1014.        begin
  1015.           DIB_SetColor(AnimatedColorIndex[Color]);
  1016.           DIB_FillEllipse(X+W div 2,Y + H div 2,W div 2,H div 2);
  1017.        end;
  1018.     end;
  1019.     procedure DrawZone(X,Y,W,H: Integer; Zone: Integer; Color: Integer);
  1020.     var
  1021.        HDelta, VDelta: Integer;
  1022.     begin
  1023.         HDelta := Trunc(Zone * ((W/ZoneCount)/2));
  1024.         VDelta := Trunc(Zone * ((H/ZoneCount)/2));
  1025.         with DIBCanvas do
  1026.         begin
  1027.            DIB_SetColor(AnimatedColorIndex[Color]);
  1028.            DIB_FillEllipse(X+W div 2,Y+H div 2,(W-HDelta*2) div 2,(H-VDelta*2) div 2);
  1029.         end;
  1030.     end;
  1031.     function EllipseRect(i: Integer ): TRect;
  1032.     var
  1033.         X, Y: Integer;
  1034.     begin
  1035.        if Arrange = laLine then
  1036.           if Vert then
  1037.             Result := Bounds(ERect.Left + Delta, ERect.Top + i*2*Radius + (2*i+1)* Delta, 2*Radius, 2*Radius)
  1038.           else
  1039.             Result := Bounds(ERect.Left + i*2*Radius + (2*i+1)* Delta, ERect.Top + Delta, 2*Radius, 2*Radius)
  1040.        else
  1041.        begin
  1042.           case i of
  1043.              0 : begin X := AWidth div 2 - Radius - Delta; Y := Radius + Delta; end;
  1044.              1 : begin X := AWidth div 2; Y := AHeight - Delta - Radius; end;
  1045.              2 : begin X := AWidth div 2 + Radius + Delta; Y := Radius + Delta; end;
  1046.            else
  1047.               Exit; {???}
  1048.           end;
  1049.           Result := Bounds(X+ERect.Left-Radius,Y+ERect.Top-Radius,2*Radius,2*Radius);
  1050.        end;
  1051.     end;
  1052. begin
  1053.     AWidth  := (FClientRect.Right-FClientRect.Left);
  1054.     AHeight := (FClientRect.Bottom-FClientRect.Top);
  1055.     Delta   := TriangleDist div 2;
  1056.     if Arrange = laLine then
  1057.     begin
  1058.        Vert    := False;
  1059.        if AHeight > AWidth then
  1060.        begin
  1061.           Vert := True;
  1062.           if (AHeight div NumLights) > AWidth then
  1063.               AHeight := AWidth * NumLights
  1064.           else
  1065.               AWidth := AHeight div NumLights;
  1066.           Radius := ((AHeight div NumLights)) div 2 - Delta;
  1067.        end
  1068.        else
  1069.        begin
  1070.           if (AWidth div NumLights) > AHeight then
  1071.               AWidth := AHeight * NumLights
  1072.           else
  1073.               AHeight := AWidth div NumLights;
  1074.           Radius := ((AWidth div NumLights)) div 2 - Delta;
  1075.        end;
  1076.     end
  1077.     else
  1078.     begin
  1079.        if (AWidth > AHeight) then
  1080.           AWidth := AHeight;
  1081.        Radius  := (AWidth - 4 * Delta) div 4;
  1082.        AWidth  := 4*(Radius+Delta);
  1083.        AHeight := Trunc((2+Sqrt(3))*(Radius+Delta));
  1084.     end;
  1085.     if Radius <= 0 then Exit;
  1086.     ERect := Bounds(((FClientRect.Right-FClientRect.Left)-AWidth) div 2,
  1087.                     ((FClientRect.Bottom-FClientRect.Top)-AHeight) div 2,
  1088.                     AWidth, AHeight);
  1089.     if (Kind = lkCircle) then
  1090.     begin
  1091.        for i := 0 to NumLights-1 do
  1092.        begin
  1093.           R := EllipseRect(i);
  1094.           DrawCircle(R.Left,R.Top,R.Right-R.Left,R.Bottom-R.Top,i);
  1095.        end;
  1096.     end
  1097.     else
  1098.     begin
  1099.        for i := 0 to NumLights-1 do
  1100.        begin
  1101.           R := EllipseRect(i);
  1102.           for j := 0 to ZoneCount - 1 do
  1103.               DrawZone(R.Left,R.Top,R.Right-R.Left,
  1104.                        R.Bottom-R.Top,j,i*ZoneCount+j);
  1105.        end;
  1106.     end;
  1107. end;
  1108. {-- TMMLight ------------------------------------------------------------}
  1109. procedure TMMLight.DrawCurrentData;
  1110. var
  1111.     i       : integer;
  1112.     j       : integer;
  1113.     Value   : Integer;
  1114.     function RGBColor(Index: Integer; Value: Integer): TColor;
  1115.     begin
  1116.        Result := 0;
  1117.        case i of
  1118.           0 : Result := RGB(Value,0,0);
  1119.           1 : Result := RGB(0,Value,0);
  1120.           2 : Result := RGB(Value,Value,0);
  1121.        end;
  1122.     end;
  1123.     function LightColor(i: Integer; Value: Integer): TColor;
  1124.     begin
  1125.        Result:= RGBColor(i,Value);
  1126.     end;
  1127.     function ZoneColor(i: Integer; Zone: Integer; Value: Integer): TColor;
  1128.     var
  1129.        X, Y: Integer;
  1130.        ZoneUpper: Integer;
  1131.     begin
  1132.        X  := (ZoneCount - Zone - 1);
  1133.        if X > ZoneCount*SphereHorz then
  1134.           X := Trunc(ZoneCount*SphereHorz);
  1135.        if (SphereHorz = 0) or (SphereVert = 0) then
  1136.           Value := 0
  1137.        else
  1138.        begin
  1139.           Y        := Trunc(Sqrt(Sqr(ZoneCount)-Sqr(X/SphereHorz))*SphereVert);
  1140.           ZoneUpper:= Trunc((Y/(ZoneCount*SphereVert))*255);
  1141.           Value    := Trunc((Value/255)*ZoneUpper);
  1142.        end;
  1143.        Result := RGBColor(i,Value);
  1144.     end;
  1145. begin
  1146.    CalcDisplayValues;
  1147.    DIBCanvas.BeginAnimate;
  1148.    try
  1149.       for i := 0 to NumLights - 1 do
  1150.       begin
  1151.          case i of
  1152.               0: Value := Trunc(FValues^[i].CurValue * (FGainBass));
  1153.               1: Value := Trunc(FValues^[i].CurValue * (2*FGainMiddle));
  1154.               2: Value := Trunc(FValues^[i].CurValue * (4*FGainTreble));
  1155.            else  Value := 0;
  1156.          end;
  1157.          Value := MinMax(Value,0,255);
  1158.          if (Value <> FValues^[i].OldValue) then
  1159.          begin
  1160.             FValues^[i].OldValue := Value;
  1161.             with DIBCanvas do
  1162.             if Kind = lkCircle then
  1163.                AnimatedColorValue[i] := LightColor(i,Value)
  1164.             else
  1165.                for j := 0 to ZoneCount - 1 do
  1166.                AnimatedColorValue[ZoneCount*i+j] := ZoneColor(i,j,Value);
  1167.          end;
  1168.       end;
  1169.    finally
  1170.       DIBCanvas.EndAnimate;
  1171.    end;
  1172. end;
  1173. {-- TMMLight ------------------------------------------------------------}
  1174. function TMMLight.GetPalette: HPALETTE;
  1175. begin
  1176.    Result := DIBCanvas.Palette;
  1177. end;
  1178. {-- TMMLight ------------------------------------------------------------}
  1179. procedure TMMLight.DrawLight;
  1180. begin
  1181.    SelectPalette(Canvas.Handle,DIBCanvas.Palette,True);
  1182.    DrawCurrentData;
  1183.    DIBCanvas.DIB_BitBlt(Canvas.Handle, FClientRect,0,0);
  1184. end;
  1185. {-- TMMLight ------------------------------------------------------------}
  1186. Procedure TMMLight.Paint;
  1187. begin
  1188.    { draw the Bevel }
  1189.    Bevel.PaintBevel(Canvas, ClientRect,True);
  1190.    DrawLight;
  1191.    {$IFDEF BUILD_ACTIVEX}
  1192.    if Selected then
  1193.    begin
  1194.       Canvas.Brush.Style := bsClear;
  1195.       Canvas.Pen.Color   := clRed;
  1196.       Canvas.Rectangle(0,0,Width,Height);
  1197.       Canvas.Brush.Style := bsSolid;
  1198.    end;
  1199.    {$ENDIF}
  1200. end;
  1201. end.