Add audio buffer setting and optimize overlays

This commit is contained in:
2026-05-22 18:07:05 +03:00
parent 26aa55ddde
commit c5f225332b
6 changed files with 327 additions and 38 deletions
+153 -11
View File
@@ -21,6 +21,7 @@ type
FOnSelect: TModeFilterEvent;
FOnDSPChange: TDSPChangeEvent;
FOnAGCChange: TAGCChangeEvent;
FOnInvalidate: TNotifyEvent;
FModeFilter: array[0..7] of Integer;
FNRMode: Integer; // 0=off, 1..4=NR/NR2/NR3/NR4
@@ -34,6 +35,8 @@ type
FHitActions: array[0..24] of Integer;
FHitCount: Integer;
FHotIdx: Integer;
FCacheBitmap: TBitmap;
FCacheDirty: Boolean;
procedure DrawSelf(C: TCanvas; W, H: Integer);
procedure DrawSMeterBar(C: TCanvas; X, Y, BW, BH: Integer);
@@ -47,6 +50,7 @@ type
function GetFilterBWForMode(ModeIdx, FilterIdx: Integer): Integer;
function GetFilterLblForMode(ModeIdx, FilterIdx: Integer): string;
function GetFilterCountForMode(ModeIdx: Integer): Integer;
procedure RequestInvalidate;
protected
procedure Paint; override;
@@ -57,19 +61,28 @@ type
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure DrawOverlay(Target: TBitmap; W, H: Integer);
function HandleMouseDown(Button: TMouseButton; X, Y: Integer): Boolean;
function HandleMouseMove(X, Y: Integer): Boolean;
function HandleMouseLeave: Boolean;
procedure SetState(AMode, ABW: Integer; AVfoHz, ASMeterDB: Double);
procedure SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean;
AAGCMode: Integer);
procedure UpdateSMeter(DB: Double);
procedure UpdateVfo(AVfoHz: Double);
property HotIdx: Integer read FHotIdx;
property OnSelect: TModeFilterEvent read FOnSelect write FOnSelect;
property OnDSPChange: TDSPChangeEvent read FOnDSPChange write FOnDSPChange;
property OnAGCChange: TAGCChangeEvent read FOnAGCChange write FOnAGCChange;
property OnInvalidate: TNotifyEvent read FOnInvalidate write FOnInvalidate;
end;
implementation
const
KEY_COLOR = TColor($00FF00FF);
HIT_NR = -9;
HIT_NB = -10;
HIT_SNB = -11;
@@ -126,6 +139,53 @@ const
// S-шкала: S1..S9
DB_S: array[1..9] of Double = (-121,-115,-109,-103,-97,-91,-85,-79,-73);
procedure BlendBitmapKey(Target, Source: TBitmap; DstX, DstY: Integer; Alpha: Byte);
var
Y, X, SX0, SY0, SX1, SY1, TY, InvA: Integer;
SrcRow, DstRow: PByte;
begin
if (Target = nil) or (Source = nil) or (Alpha = 0) then Exit;
SX0 := 0;
SY0 := 0;
SX1 := Source.Width;
SY1 := Source.Height;
if DstX < 0 then begin SX0 := -DstX; DstX := 0; end;
if DstY < 0 then begin SY0 := -DstY; DstY := 0; end;
if DstX + (SX1 - SX0) > Target.Width then SX1 := SX0 + Target.Width - DstX;
if DstY + (SY1 - SY0) > Target.Height then SY1 := SY0 + Target.Height - DstY;
if (SX1 <= SX0) or (SY1 <= SY0) then Exit;
InvA := 255 - Alpha;
Target.BeginUpdate(False);
Source.BeginUpdate(False);
try
for Y := SY0 to SY1 - 1 do
begin
TY := DstY + (Y - SY0);
SrcRow := PByte(Source.ScanLine[Y]);
DstRow := PByte(Target.ScanLine[TY]);
if (SrcRow = nil) or (DstRow = nil) then Continue;
Inc(SrcRow, SX0 * 4);
Inc(DstRow, DstX * 4);
for X := SX0 to SX1 - 1 do
begin
// KEY_COLOR is clFuchsia in pf32bit B,G,R order.
if not ((SrcRow[0] = $FF) and (SrcRow[1] = $00) and (SrcRow[2] = $FF)) then
begin
DstRow[0] := Byte((Alpha * SrcRow[0] + InvA * DstRow[0]) div 255);
DstRow[1] := Byte((Alpha * SrcRow[1] + InvA * DstRow[1]) div 255);
DstRow[2] := Byte((Alpha * SrcRow[2] + InvA * DstRow[2]) div 255);
end;
Inc(SrcRow, 4);
Inc(DstRow, 4);
end;
end;
finally
Source.EndUpdate(False);
Target.EndUpdate(False);
end;
end;
function TVfoOverlay.GetFilterBWForMode(ModeIdx, FilterIdx: Integer): Integer;
begin
case ModeIdx of
@@ -206,11 +266,20 @@ begin
FANFOn := False;
FAGCMode := 0;
FAGCPickerOpen := False;
FCacheBitmap := TBitmap.Create;
FCacheBitmap.PixelFormat := pf32bit;
FCacheDirty := True;
Width := 260;
Height := OVL_H_NORM;
Cursor := crHandPoint;
end;
destructor TVfoOverlay.Destroy;
begin
FCacheBitmap.Free;
inherited Destroy;
end;
procedure TVfoOverlay.RegisterHit(HR: TRect; HV: Integer);
begin
if FHitCount > High(FHitRects) then Exit;
@@ -219,6 +288,13 @@ begin
Inc(FHitCount);
end;
procedure TVfoOverlay.RequestInvalidate;
begin
FCacheDirty := True;
inherited Invalidate;
if Assigned(FOnInvalidate) then FOnInvalidate(Self);
end;
function TVfoOverlay.FormatFreq(Hz: Double): string;
var
iM, iK, iH: Integer;
@@ -534,6 +610,72 @@ begin
DrawSelf(Canvas, Width, Height);
end;
procedure TVfoOverlay.DrawOverlay(Target: TBitmap; W, H: Integer);
begin
if (not Visible) or (Target = nil) or (Width <= 0) or (Height <= 0) then Exit;
if (Left >= W) or (Top >= H) then Exit;
if (FCacheBitmap.Width <> Width) or (FCacheBitmap.Height <> Height) then
begin
FCacheBitmap.SetSize(Width, Height);
FCacheDirty := True;
end;
if FCacheDirty then
begin
FCacheBitmap.Canvas.Brush.Color := KEY_COLOR;
FCacheBitmap.Canvas.Brush.Style := bsSolid;
FCacheBitmap.Canvas.Pen.Style := psClear;
FCacheBitmap.Canvas.FillRect(Rect(0, 0, Width, Height));
DrawSelf(FCacheBitmap.Canvas, Width, Height);
FCacheDirty := False;
end;
BlendBitmapKey(Target, FCacheBitmap, Left, Top, 218);
end;
function TVfoOverlay.HandleMouseDown(Button: TMouseButton; X, Y: Integer): Boolean;
begin
Result := False;
if not Visible then Exit;
if (X < Left) or (Y < Top) or (X >= Left + Width) or (Y >= Top + Height) then Exit;
MouseDown(Button, [], X - Left, Y - Top);
Result := True;
end;
function TVfoOverlay.HandleMouseMove(X, Y: Integer): Boolean;
var
I, OldHot, LX, LY: Integer;
begin
Result := False;
if not Visible then Exit;
LX := X - Left;
LY := Y - Top;
OldHot := FHotIdx;
FHotIdx := -1;
if (LX >= 0) and (LY >= 0) and (LX < Width) and (LY < Height) then
for I := 0 to FHitCount - 1 do
if PtInRect(FHitRects[I], Point(LX, LY)) then
begin
FHotIdx := I;
Break;
end;
if FHotIdx <> OldHot then
begin
RequestInvalidate;
Result := True;
end;
end;
function TVfoOverlay.HandleMouseLeave: Boolean;
begin
Result := False;
if FHotIdx >= 0 then
begin
FHotIdx := -1;
RequestInvalidate;
Result := True;
end;
end;
procedure TVfoOverlay.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
var
@@ -556,7 +698,7 @@ begin
FModeFilter[FMode] := FFilterBW;
FMode := NewMode;
FFilterBW := FModeFilter[FMode];
Invalidate;
RequestInvalidate;
if Assigned(FOnSelect) then FOnSelect(FMode, FFilterBW);
end;
end
@@ -568,7 +710,7 @@ begin
FAGCPickerOpen := not FAGCPickerOpen;
if FAGCPickerOpen then Height := OVL_H_AGC
else Height := OVL_H_NORM;
Invalidate;
RequestInvalidate;
end
else
begin
@@ -578,7 +720,7 @@ begin
HIT_SNB: FSNBOn := not FSNBOn;
HIT_ANF: FANFOn := not FANFOn;
end;
Invalidate;
RequestInvalidate;
if Assigned(FOnDSPChange) then
FOnDSPChange(FNRMode, FNBMode, FSNBOn, FANFOn);
end;
@@ -590,7 +732,7 @@ begin
FAGCMode := HV - HIT_AGC_PICK_BASE;
FAGCPickerOpen := False;
Height := OVL_H_NORM;
Invalidate;
RequestInvalidate;
if Assigned(FOnAGCChange) then FOnAGCChange(FAGCMode);
end
else if HV > 0 then
@@ -600,7 +742,7 @@ begin
begin
FFilterBW := NewBW;
FModeFilter[FMode] := FFilterBW;
Invalidate;
RequestInvalidate;
if Assigned(FOnSelect) then FOnSelect(FMode, FFilterBW);
end;
end;
@@ -617,13 +759,13 @@ begin
for I := 0 to FHitCount - 1 do
if PtInRect(FHitRects[I], Point(X, Y)) then
begin FHotIdx := I; Break; end;
if FHotIdx <> OldHot then Invalidate;
if FHotIdx <> OldHot then RequestInvalidate;
end;
procedure TVfoOverlay.MouseLeave;
begin
inherited;
if FHotIdx >= 0 then begin FHotIdx := -1; Invalidate; end;
if FHotIdx >= 0 then begin FHotIdx := -1; RequestInvalidate; end;
end;
procedure TVfoOverlay.SetState(AMode, ABW: Integer; AVfoHz, ASMeterDB: Double);
@@ -634,7 +776,7 @@ begin
FModeFilter[FMode] := ABW;
FVfoHz := AVfoHz;
FSMeterDB := ASMeterDB;
Invalidate;
RequestInvalidate;
end;
procedure TVfoOverlay.SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean;
@@ -645,7 +787,7 @@ begin
FSNBOn := ASNBOn;
FANFOn := AANFOn;
FAGCMode := AAGCMode;
Invalidate;
RequestInvalidate;
end;
procedure TVfoOverlay.UpdateSMeter(DB: Double);
@@ -654,7 +796,7 @@ begin
if Abs(DB - FSMeterDB) > 0.4 then
begin
FSMeterDB := DB;
Invalidate;
RequestInvalidate;
end;
end;
@@ -663,7 +805,7 @@ begin
if FVfoHz <> AVfoHz then
begin
FVfoHz := AVfoHz;
Invalidate;
RequestInvalidate;
end;
end;