home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-06-10 | 19.0 KB | 809 lines | [TEXT/CWIE] |
- unit fft;
-
- {
- This file contains a Pascal language implementation of the
- Fast Hartley Transform algorithm which is covered under
- United States Patent Number 4,646,256.
-
- This code may therefore be freely used and distributed only
- under the following conditions:
-
- 1) This header is included in every copy of the code; and
- 2) The code is used for noncommercial research purposes only.
-
- Firms using this code for commercial purposes will be infringing a United
- States patent and should contact the
-
- Office of Technology Licensing
- Stanford University
- 857 Serra Street, 2nd Floor
- Stanford, CA 94305-6225
- (415) 723 0651
-
- This implementation is based on Pascal code contibuted
- by Arlo Reeves (arlo@apple.com).
- }
-
- interface
- uses
- Types, Memory, QuickDraw, QuickDrawText, Packages, Menus, Events, Fonts,
- Scrap, ToolUtils, Resources, Errors, Palettes, StandardFile, Windows,
- Controls, TextEdit, Files, Dialogs, TextUtils, Finder, MixedMode, Processes,
- globals, Utilities, Graphics, Filters, Analysis;
-
- procedure doFFT(fftKind: fftType);
- procedure DisplayPowerSpectrum(x: rImagePtr);
- function isPowerOf2 (x: LongInt): boolean;
- procedure SetFFTWindowName(doInverse: boolean);
- procedure RedisplayPowerSpectrum;
- procedure doSwapQuadrants;
- function isFFT: boolean;
- procedure ShowFFTValues (hloc, vloc, ivalue: LongInt);
-
-
- implementation
-
- const
- UpdateTicks = 10; {Show progress 6 times/sec}
-
- var
- AbortFFT: boolean;
- C, S: rLinePtr;
-
-
- function log2 (x: LongInt): LongInt;
- var
- count: LongInt;
- begin
- count := 15;
- while not BTST(x, count) do
- count := count - 1;
- log2 := count;
- end;
-
-
- function BitRevX (x, bitlen: LongInt): LongInt;
- var
- i: LongInt;
- temp: longint;
- begin
- temp := 0;
- for i := 0 to bitlen do
- if BTST(x, i) then
- BSET(temp, bitlen - i - 1);
- BitRevX := LoWord(temp);
- end;
-
-
- procedure BitRevRArr (x: rLinePtr; bitlen, maxN: LongInt);
- var
- i: LongInt;
- tempArr: rLineType;
- begin
- for i := 0 to maxN - 1 do
- tempArr[i] := x^[BitRevX(i, bitlen)];
- BlockMove(@tempArr, x, maxN * SizeOf(real));
- end;
-
-
- procedure transposeR (x: rImagePtr; maxN: LongInt);
- var
- r, c: LongInt;
- rTemp: real;
- begin
- for r := 0 to maxN - 1 do
- for c := r to maxN - 1 do
- if r <> c then
- begin
- rTemp := x^[ r * maxN + c];
- x^[ r * maxN + c] := x^[c * maxN + r];
- x^[c * maxN + r] := rTemp;
- end;
- end;
-
-
- procedure FHTps (r, maxN: LongInt; inMat: rImagePtr; var outArr: rLineType);
- { Power Spectrum of one row from 2D Hartley Transform }
- var
- c, base: LongInt;
- begin
- base := r * maxN;
- for c := 0 to maxN - 1 do
- outArr[c] := (sqr(inMat^[base + c]) + sqr(inMat^[((maxN - r) mod maxN) * maxN + (maxN - c) mod maxN])) / 2;
- end;
-
-
- procedure dfht3 (x: rLinePtr; inverse: boolean; maxN: LongInt);
- { an optimized real FHT }
- var
- i, stage, gpNum, gpIndex, gpSize, numGps, Nlog2: LongInt;
- bfNum, numBfs: LongInt;
- Ad0, Ad1, Ad2, Ad3, Ad4, CSAd: LongInt;
- rt1, rt2, rt3, rt4: real;
- begin
- Nlog2 := log2(maxN);
- BitRevRArr(x, Nlog2, maxN); { bitReverse the input array }
-
- gpSize := 2; { first & second stages - do radix 4 butterflies once thru }
- numGps := maxN div 4;
- for gpNum := 0 to numGps - 1 do
- begin
- Ad1 := gpNum * 4;
- Ad2 := Ad1 + 1;
- Ad3 := Ad1 + gpSize;
- Ad4 := Ad2 + gpSize;
- rt1 := x^[Ad1] + x^[Ad2]; { a + b }
- rt2 := x^[Ad1] - x^[Ad2]; { a - b }
- rt3 := x^[Ad3] + x^[Ad4]; { c + d }
- rt4 := x^[Ad3] - x^[Ad4]; { c - d }
- x^[Ad1] := rt1 + rt3; { a + b + (c + d ) }
- x^[Ad2] := rt2 + rt4; { a - b + (c - d ) }
- x^[Ad3] := rt1 - rt3; { a + b - (c + d ) }
- x^[Ad4] := rt2 - rt4; { a - b - (c - d ) }
- end;
-
- if Nlog2 > 2 then
- begin { third + stages computed here }
- gpSize := 4;
- numBfs := 2;
- numGps := numGps div 2;
- for stage := 2 to Nlog2 - 1 do
- begin
- for gpNum := 0 to numGps - 1 do
- begin
- Ad0 := gpNum * gpSize * 2;
- Ad1 := Ad0; { 1st butterfly is different from others - no mults needed }
- Ad2 := Ad1 + gpSize;
- Ad3 := Ad1 + gpSize div 2;
- Ad4 := Ad3 + gpSize;
- rt1 := x^[Ad1];
- x^[Ad1] := x^[Ad1] + x^[Ad2];
- x^[Ad2] := rt1 - x^[Ad2];
- rt1 := x^[Ad3];
- x^[Ad3] := x^[Ad3] + x^[Ad4];
- x^[Ad4] := rt1 - x^[Ad4];
- for bfNum := 1 to numBfs - 1 do
- begin { subsequent BF's dealt with together }
- Ad1 := bfNum + Ad0;
- Ad2 := Ad1 + gpSize;
- Ad3 := gpSize - bfNum + Ad0;
- Ad4 := Ad3 + gpSize;
-
- CSAd := bfNum * numGps;
- rt1 := x^[Ad2] * C^[CSAd] + x^[Ad4] * S^[CSAd];
- rt2 := x^[Ad4] * C^[CSAd] - x^[Ad2] * S^[CSAd];
-
- x^[Ad2] := x^[Ad1] - rt1;
- x^[Ad1] := x^[Ad1] + rt1;
- x^[Ad4] := x^[Ad3] + rt2;
- x^[Ad3] := x^[Ad3] - rt2;
-
- end; { for bfNum := 0 to ... }
- end; { for gpNum := 0 to ... }
- gpSize := gpSize * 2;
- numBfs := numBfs * 2;
- numGps := numGps div 2;
- end;
- end; { if }
-
- if inverse then
- for i := 0 to maxN - 1 do
- x^[i] := x^[i] / maxN;
- end;
-
-
- procedure rc2Dfht (x: rImagePtr; inverse: boolean; maxN: LongInt);
- { Row-column 2D FHT }
- var
- row, col, mRow, mCol, NextTicks: LongInt;
- A, B, C, D, E: real;
- theRow: rLinePtr;
- begin
- NextTicks := TickCount + UpdateTicks;
- for row := 0 to maxN - 1 do begin
- theRow := rLinePtr(ord4(x) + row * maxN * SizeOf(real));
- dfht3(theRow, inverse, maxN);
- if TickCount > nextTicks then begin
- UpdateMeter(round(row/maxN * 50.0), 'Computing FHT');
- nextTicks := TickCount + updateTicks;
- if CommandPeriod then begin
- beep;
- AbortFFT := true;
- exit(rc2Dfht)
- end;
- end;
- end;
- transposeR(x, maxN);
- for row := 0 to maxN - 1 do begin
- theRow := rLinePtr(ord4(x) + row * maxN * SizeOf(real));
- dfht3(theRow, inverse, maxN);
- if TickCount > nextTicks then begin
- UpdateMeter(round(row/maxN * 50.0) + 50, 'Computing FHT');
- nextTicks := TickCount + updateTicks;
- if CommandPeriod then begin
- beep;
- AbortFFT := true;
- exit(rc2Dfht)
- end;
- end;
- end;
- transposeR(x, maxN);
- for row := 0 to maxN div 2 do { Now calculate actual Hartley transform }
- for col := 0 to maxN div 2 do
- begin
- mRow := (maxN - row) mod maxN;
- mCol := (maxN - col) mod maxN;
- A := x^[row * maxN + col]; { see Bracewell, 'Fast 2D Hartley Transf.' IEEE Procs. 9/86 }
- B := x^[mRow * maxN + col];
- C := x^[row * maxN + mCol];
- D := x^[mRow * maxN + mCol];
- E := ((A + D) - (B + C)) / 2;
- x^[row * maxN + col] := A - E;
- x^[mRow * maxN + col] := B + E;
- x^[row * maxN + mCol] := C + E;
- x^[mRow * maxN + mCol] := D - E;
- end;
- UpdateMeter(-1, 'Hide Meter');
- end;
-
-
- procedure SwapQuadrants;
- {Swap quadrants 1 and 3 and quadrants 2 and 4 so the}
- {power spectrum origin is at the center of the image.}
- {2 1}
- {3 4}
- var
- row, col, halfMaxN, tmp, maxN: LongInt;
- line1, line2: LineType;
- begin
- maxN := info^.PixelsPerLine;
- halfMaxN := maxN div 2;
- for row:= 0 to halfMaxN -1 do begin
- GetLine(0, row, maxN, line1);
- GetLine(0, row + halfMaxN, maxN, line2);
- for col := 0 to halfMaxN -1 do begin
- tmp := line1[col];
- line1[col] := line1[col + halfMaxN];
- line1[col + halfMaxN] := tmp;
- tmp := line2[col];
- line2[col] := line2[col + halfMaxN];
- line2[col + halfMaxN] := tmp;
- end;
- PutLine(0, row, maxN, line2);
- PutLine(0, row + halfMaxN, maxN, line1);
- end;
- end;
-
-
-
- procedure DisplayRealImage(x: rImagePtr);
- var
- row, col, i, base, maxN: LongInt;
- r, min, max, scale: real;
- line: lineType;
- begin
- maxN := info^.PixelsPerLine;
- min := 1e99;
- max := -1e99;
- i := 1;
- for row := 0 to maxN - 1 do begin
- base := row * maxN;
- for col := 0 to maxN - 1 do begin
- r := x^[base + col];
- if r < min then min := r;
- if r > max then max := r;
- end;
- end;
- scale := 253.0 / (max - min);
- for row := 0 to maxN - 1 do begin
- base := row * maxN;
- for col := 0 to maxN - 1 do begin
- r := x^[base + col];
- line[col] := round((r - min) * scale) + 1;
- end;
- PutLine(0, row, maxN, line);
- end;
- end;
-
-
- procedure DisplayPSUsingBuffer(fht, ps: rImagePtr; var rLine: rLineType);
- var
- row, col, base, nextTicks, maxN: LongInt;
- r, min, max, scale: real;
- line: lineType;
-
- begin
- maxN := info^.PixelsPerLine;
- nextTicks := TickCount + updateTicks;
- min := 1e99;
- max := -1e99;
- for row := 0 to maxN - 1 do begin
- FHTps (row, maxN, fht, rLine);
- base := row * maxN;
- for col := 0 to maxN - 1 do begin
- r := rLine[col];
- if r < min then min := r;
- if r > max then max := r;
- ps^[base + col] := r;
- end;
- if TickCount > nextTicks then begin
- UpdateMeter(round(row/maxN * 80.0), 'Computing Power Spectrum');
- nextTicks := TickCount + updateTicks;
- if CommandPeriod then begin
- beep;
- AbortFFT := true;
- exit(DisplayPSUsingBuffer)
- end;
- end;
- end;
- if min < 1.0 then
- min := 0.0
- else
- min := ln(min);
- max := ln(max);
- scale := 253.0 / (max - min);
- for row := 0 to maxN - 1 do begin
- base := row * maxN;
- for col := 0 to maxN - 1 do begin
- r := ps^[base + col];
- if r < 1.0 then
- r := 0.0
- else
- r := ln(r);
- line[col] := round((r - min) * scale) + 1;
- end;
- PutLine(0, row, maxN, line);
- if TickCount > nextTicks then begin
- UpdateMeter(round(row/maxN * 20.0 ) + 80, 'Computing Power Spectrum');
- nextTicks := TickCount + updateTicks;
- if CommandPeriod then begin
- beep;
- AbortFFT := true;
- exit(DisplayPSUsingBuffer)
- end;
- end;
- end;
- SwapQuadrants;
- UpdateMeter(-1, 'Hide Meter');
- end;
-
-
- procedure DisplayPowerSpectrum(fht: rImagePtr);
- var
- row, col, nextTicks, maxN: LongInt;
- r, min, max, scale: real;
- line: lineType;
- rLine: rLineType;
- tempH: handle;
- ps: rImagePtr;
- begin
- maxN := info^.PixelsPerLine;
- tempH := GetBigHandle(maxN * maxN * SizeOf(real));
- if tempH <> nil then begin
- hlock(tempH);
- ps := rImagePtr(tempH^);
- DisplayPSUsingBuffer(fht, ps, rLine);
- hunlock(tempH);
- DisposeHandle(tempH);
- exit(DisplayPowerSpectrum);
- end;
- min := 1e99;
- max := -1e99;
- nextTicks := TickCount + updateTicks;
- for row := 0 to maxN - 1 do begin
- FHTps (row, maxN, fht, rLine);
- for col := 0 to maxN - 1 do begin
- r := rLine[col];
- if r < min then min := r;
- if r > max then max := r;
- end;
- if TickCount > nextTicks then begin
- UpdateMeter(round(row/maxN * 35.0), 'Computing Power Spectrum');
- nextTicks := TickCount + updateTicks;
- if CommandPeriod then begin
- beep;
- AbortFFT := true;
- exit(DisplayPowerSpectrum)
- end;
- end;
- end;
- if min < 1.0 then
- min := 0.0
- else
- min := ln(min);
- max := ln(max);
- scale := 253.0 / (max - min);
- for row := 0 to maxN - 1 do begin
- FHTps (row, maxN, fht, rLine);
- for col := 0 to maxN - 1 do begin
- r := rLine[col];
- if r < 1.0 then
- r := 0.0
- else
- r := ln(r);
- line[col] := round((r - min) * scale) + 1;
- end;
- PutLine(0, row, maxN, line);
- if TickCount > nextTicks then begin
- UpdateMeter(round(row/maxN * 65.0 ) + 35, 'Computing Power Spectrum');
- nextTicks := TickCount + updateTicks;
- if CommandPeriod then begin
- beep;
- AbortFFT := true;
- exit(DisplayPowerSpectrum)
- end;
- end;
- end;
- SwapQuadrants;
- UpdateMeter(-1, 'Hide Meter');
- end;
-
-
- procedure ConvertToReal;
- var
- row, col, i, sum, base: LongInt;
- width, height: LongInt;
- mean, pixelCount: real;
- line: LineType;
- DataP: rImagePtr;
- begin
- with info^ do begin
- width := pixelsPerLine;
- height := nLines;
- hlock(DataH);
- DataP := rImagePtr(DataH^);
- end;
- UpdateMeter(0, 'Converting to Real');
- {
- GetRectHistogram;
- sum := 0;
- pixelCount := width * height;
- for i := 0 to 255 do
- sum := sum + histogram[i] * i;
- mean := sum / pixelCount;
- }
- for row:= 0 to height - 1 do begin
- GetLine(0, row, width, line);
- base := row * width;
- for col := 0 to width - 1 do
- DataP^[base + col] := line[col] {- mean};
- end;
- hunlock(info^.DataH);
- end;
-
-
- function isPowerOf2 (x: LongInt): boolean;
- var
- i, sum: integer;
- begin
- sum := 0;
- x := abs(x);
- for i := 0 to 15 do
- sum := sum + ord(BTST(x, i));
- IsPowerOf2 := (sum <= 1);
- end;
-
-
- function PowerOf2Size: boolean;
- var
- width, height: LongInt;
-
- procedure fail;
- begin
- PutError('A square, power of two size image or selection (128x128, 256x256, etc.) required.');
- AbortMacro;
- PowerOf2Size := false;
- exit(PowerOf2Size);
- end;
-
- begin
- with info^ do begin
- if info = noInfo then
- fail;
- width := pixelsPerLine;
- height := nLines;
- if RoiShowing and (roiType = rectRoi) then with roiRect do begin
- width := right - left;
- height := bottom - top;
- end;
- if not isPowerOf2(width) or (width <> height) then
- fail;
- if width < 4 then begin
- PutMessage('Sequence Length must be >= 4.');
- PowerOf2Size := true;
- exit(PowerOf2Size);
- end;
- PowerOf2Size := true;
- end;
- end;
-
-
- function MakeSinCosTables(maxN: LongInt): boolean;
- var
- i: LongInt;
- theta, dTheta: real;
- begin
- C := rLinePtr(NewPtr(SizeOf(rLineType)));
- S := rLinePtr(NewPtr(SizeOf(rLineType)));
- if (C = nil) or (S = nil) then begin
- MakeSinCosTables := false;
- PutError('Out of Memory');
- exit(MakeSinCosTables);
- end;
- theta := 0;
- dTheta := 2 * pi / maxN;
- for i := 0 to maxN div 4 - 1 do
- begin
- C^[i] := cos(theta);
- S^[i] := sin(theta);
- theta := theta + dTheta;
- end;
- MakeSinCosTables := true;
- end;
-
-
- function MakeRealImage: boolean;
- var
- TempH: handle;
- maxN: LongInt;
- begin
- maxN := info^.PixelsPerLine;
- tempH := GetBigHandle(maxN * maxN * SizeOf(real));
- if TempH = nil then begin
- PutMemoryAlert;
- MakeRealImage := false;
- exit(MakeRealImage);
- end;
- if not Duplicate(StringOf('FFT ', nPics + 1:1), false) then begin
- DisposeHandle(TempH);
- exit(MakeRealImage);
- end;
- UpdatePicWindow;
- info^.DataH := tempH;
- ConvertToReal;
- UpdateTitleBar;
- MakeRealImage := true;
- end;
-
-
- procedure SetFFTWindowName(doInverse: boolean);
- begin
- with info^ do begin
- if doInverse then
- title := StringOf('Inverse FFT ', picNum:1)
- else
- title := StringOf('FFT ', picNum:1);
- UpdateWindowsMenuItem;
- UpdateTitleBar;
- end;
- end;
-
-
- procedure ApplyFilter(rData: rImagePtr);
- var
- row, col, width, height, base, i: LongInt;
- line: LineType;
- passMode: boolean;
- t:FateTable;
- begin
- SwapQuadrants;
- with info^ do begin
- width := pixelsPerLine;
- height := nLines;
- end;
- for row:= 0 to height - 1 do begin
- GetLine(0, row, width, line);
- base := row * width;
- for col := 0 to width - 1 do
- rData^[base + col] := line[col]/255.0 * rData^[base + col];
- end;
- end;
-
-
- procedure doMasking(rData: rImagePtr);
- var
- row, col, width, height, base, i: LongInt;
- line: LineType;
- passMode: boolean;
- t:FateTable;
- begin
- GetRectHistogram;
- if (histogram[0] = 0) and (histogram[255] = 0) then
- exit(doMasking);
- UpdateMeter(0, 'Masking');
- passMode := histogram[255] <> 0;
- if passMode then
- ChangeValues(0,254,0)
- else
- ChangeValues(1,255,255);
- for i := 1 to 3 do
- Filter(UnweightedAvg, 0, t);
- UpdatePicWindow;
- ApplyFilter(rData);
- end;
-
-
- procedure doFFT(fftKind: fftType);
- var
- startTicks, maxN: LongInt;
- trect: rect;
- RealData: rImagePtr;
- doInverse: boolean;
- begin
- doInverse := fftKind <> ForewardFFT;
- if not PowerOf2Size then
- exit(doFFT);
- startTicks := tickCount;
- if info^.DataH = nil then begin
- if doInverse then begin
- PutError('A real image is required to do an inverse transform.');
- AbortMacro;
- exit(doFFT);
- end;
- if not MakeRealImage then begin
- AbortMacro;
- exit(doFFT);
- end
- end else begin
- KillRoi;
- SetFFTWindowName(doInverse);
- end;
- hlock(info^.DataH);
- RealData := rImagePtr(info^.DataH^);
- ShowWatch;
- maxN := info^.PixelsPerLine;
- if not MakeSinCosTables(maxN) then
- exit(doFFT);
- AbortFFT := false;
- ShowMessage(CmdPeriodToStop);
- if doInverse then begin
- if fftKind = InverseFFTWithMask then
- doMasking(RealData)
- else if fftKind = InverseFFTWithFilter then
- ApplyFilter(RealData);
- rc2DFHT(RealData, true, maxN);
- if not AbortFFT then
- DisplayRealImage(RealData);
- end else begin
- rc2DFHT(RealData, false, maxN);
- if not AbortFFT then
- DisplayPowerSpectrum(RealData);
- end;
- if AbortFFT then
- UpdateMeter(-1, 'Hide');
- hunlock(info^.dataH);
- UpdatePicWindow;
- SetRect(trect, 0, 0, maxN, maxN);
- ShowTime(startTicks, trect, '');
- UpdateWindowsMenuItem;
- DisposePtr(ptr(C));
- DisposePtr(ptr(S));
- end;
-
-
- function isFFT: boolean;
- begin
- isFFT := false;
- with info^ do
- if DataH <> nil then
- if pos('FFT', title) = 1 then
- isFFT := true;
- end;
-
-
- procedure RedisplayPowerSpectrum;
- var
- rData: rImagePtr;
- begin
- if info = noInfo then
- exit(RedisplayPowerSpectrum);
- KillRoi;
- if not PowerOf2Size then
- exit(RedisplayPowerSpectrum);
- if not isFFT then begin
- PutError('Real frequency domain image required.');
- exit(RedisplayPowerSpectrum);
- end;
- hlock(info^.DataH);
- rData := rImagePtr(info^.DataH^);
- DisplayPowerSpectrum(rData);
- hunlock(info^.dataH);
- UpdatePicWindow;
- end;
-
-
- Procedure doSwapQuadrants;
- begin
- if info = noInfo then
- exit(doSwapQuadrants);
- KillRoi;
- if not PowerOf2Size then
- exit(doSwapQuadrants);
- SetupUndo;
- WhatToUndo := UndoEdit;
- SwapQuadrants;
- UpdatePicWindow;
- end;
-
-
- function arctan2 (x, y: extended): extended;
- { returns angle in the correct quadrant }
- begin
- if x = 0 then
- x := 1E-30; { Could be improved }
- if x > 0 then
- if y >= 0 then
- arctan2 := arctan(y / x)
- else
- arctan2 := arctan(y / x) + 2 * pi
- else
- arctan2 := arctan(y / x) + pi;
- end;
-
-
- procedure ShowFFTValues (hloc, vloc, ivalue: LongInt);
- var
- tPort: GrafPtr;
- hstart, vstart: integer;
- r, theta, center: extended;
- begin
- with info^ do
- begin
- hstart := InfoHStart;
- vstart := InfoVStart;
- GetPort(tPort);
- SetPort(InfoWindow);
- TextSize(9);
- TextFont(Monaco);
- TextMode(SrcCopy);
- if hloc < 0 then
- hloc := -hloc;
- center := pixelsPerLine div 2;
- r := sqrt(sqr(hloc - center) + sqr(vloc - center));
- theta := arctan2(hloc - center, center - vloc);
- theta := theta * 180 / pi;
- MoveTo(xValueLoc, vstart);
- if SpatiallyCalibrated then begin
- DrawReal(pixelsPerLine / r / xScale, 6, 2);
- DrawString(xUnit);
- DrawString('/c ');
- DrawString('(');
- DrawReal(hloc - center, 4, 0);
- DrawString(')');
- end else begin
- DrawReal(pixelsPerLine / r, 6, 2);
- DrawString('p/c ');
- DrawString('(');
- DrawReal(hloc - center, 4, 0);
- DrawString(')');
- end;
- DrawString(' ');
- vloc := PicRect.bottom - vloc - 1;
- if vloc < 0 then
- vloc := -vloc;
- MoveTo(yValueLoc, vstart + 10);
- DrawReal(theta, 6, 2);
- TextMode(srcOr);
- DrawString('° ');
- TextMode(srcCopy);
- DrawString('(');
- DrawReal(vloc - center + 1, 4, 0);
- DrawString(')');
- DrawString(' ');
- MoveTo(zValueLoc, vstart + 20);
- if fit <> uncalibrated then
- begin
- DrawReal(cvalue[ivalue], 6, 2);
- DrawString(' (');
- DrawLong(ivalue);
- DrawString(')');
- end
- else
- DrawLong(ivalue);
- DrawString(' ');
- SetPort(tPort);
- end;
- end;
-
-
- end. {fft Unit}