mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feat(slices): multi-slice receivers (FlexRadio-style) — soft fan-out + per-slice flag/audio
Independent RX slices sharing one panadapter: each has its own freq/mode/filter/AGC/NR-NB-SNB-ANF, S-meter, volume and sound card. VFO A = main receiver; B+ are soft fan-out WDSP channels within the captured span (ideal for Pluto/QO-100; HW DDC path is Stage 3). - WDSPEngine: soft fan-out (TSliceChan, ProcessSlices in DSP thread, per-slice NCO/mode/filter/AGC/NR/NB/SNB/ANF + S-meter, OnSliceAudio), FSliceLock serialises add/remove/set vs DSP; reopened on rate change. - RadioController: TCtrlSlice with its own TAudioOutput/device, Add/Remove/SetSlice*, RefreshSliceShifts keeps slices on target Hz across centre changes. Slice letters start at 'B' (A = main receiver). - UI: per-slice flags (VfoOverlay) with letter badge/close, filter-band markers in BOTH GL and CPU renderers, drag-retune, wheel; flags placed beside the filter band with flip, like the main flag. Ctrl+click adds. - Main flag (slice A) visible whenever the left panel is hidden OR any slice exists; synced with panel-driven DSP/mode/AGC changes. - Perf: S-meter decoupled from flag chrome cache (FMeterDirty + meter-only RefreshMeter via EnsureRendered); InvalidateVfoOverlay avoids global overlay-cache churn on meter ticks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+112
-7
@@ -43,6 +43,12 @@ type
|
||||
FSampleOverlayTex: TGLTextureCache;
|
||||
FVfoOverlayTex: TGLTextureCache;
|
||||
FBandOverlayTex: TGLTextureCache;
|
||||
// Текстуры флагов слайсов (B+), привязка по указателю оверлея.
|
||||
FSliceTex: array of record
|
||||
Overlay: TVfoOverlay;
|
||||
Tex: TGLTextureCache;
|
||||
LastX, LastY, LastW, LastH: Integer;
|
||||
end;
|
||||
FLastAGCText: string;
|
||||
FLastAGCY: Integer;
|
||||
FLastAGCHangY: Integer;
|
||||
@@ -83,6 +89,9 @@ type
|
||||
procedure DrawBeaconMarkersGL(W, H: Integer);
|
||||
procedure DrawADCOverlay(W, H: Integer);
|
||||
procedure DrawCachedOverlays(W, H: Integer);
|
||||
procedure DrawSliceOverlays(W, H: Integer);
|
||||
procedure DrawSliceFilterMarkers(W, H: Integer);
|
||||
function SliceTexIndex(O: TVfoOverlay): Integer; // индекс в FSliceTex, -1 если нет
|
||||
function FormatFreqGL(Hz: Double): string;
|
||||
function ActiveVfoFrequency: Double;
|
||||
public
|
||||
@@ -96,6 +105,7 @@ type
|
||||
procedure PaintSpectrum(Sender: TObject); override;
|
||||
procedure InvalidateGridCache; override;
|
||||
procedure InvalidateOverlayCache; override;
|
||||
procedure InvalidateVfoOverlay; override;
|
||||
procedure SetTheme(const T: TAppTheme); override;
|
||||
end;
|
||||
|
||||
@@ -138,6 +148,7 @@ begin
|
||||
end;
|
||||
|
||||
destructor TSpectrumViewOpenGL.Destroy;
|
||||
var i: Integer;
|
||||
begin
|
||||
if Assigned(FGLControl) and FGLControl.MakeCurrent then
|
||||
begin
|
||||
@@ -149,7 +160,9 @@ begin
|
||||
DeleteTexture(FSampleOverlayTex);
|
||||
DeleteTexture(FVfoOverlayTex);
|
||||
DeleteTexture(FBandOverlayTex);
|
||||
for i := 0 to High(FSliceTex) do DeleteTexture(FSliceTex[i].Tex);
|
||||
end;
|
||||
SetLength(FSliceTex, 0);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
@@ -392,6 +405,13 @@ begin
|
||||
FVfoOverlayDirty := True;
|
||||
end;
|
||||
|
||||
procedure TSpectrumViewOpenGL.InvalidateVfoOverlay;
|
||||
begin
|
||||
// Только текстура главного флага — НЕ трогаем FOverlayDirty, иначе band/
|
||||
// samplerate/слайсы зря перезаливались бы на каждый тик S-метра главного.
|
||||
FVfoOverlayDirty := True;
|
||||
end;
|
||||
|
||||
procedure TSpectrumViewOpenGL.SetTheme(const T: TAppTheme);
|
||||
begin
|
||||
inherited SetTheme(T);
|
||||
@@ -536,6 +556,29 @@ begin
|
||||
DrawLine(TXVfoX, 0, TXVfoX, H - 12, TColor($002030E0), 2, False);
|
||||
DrawRect(TXVfoX - 5, 0, TXVfoX + 5, 8, TColor($002030E0), 1);
|
||||
end;
|
||||
|
||||
DrawSliceFilterMarkers(W, H);
|
||||
end;
|
||||
|
||||
// Маркер фильтра каждого слайса (B+) на спектре: полупрозрачная полоса
|
||||
// пропускания + центральная несущая. Цвет янтарный — отличить от главного.
|
||||
procedure TSpectrumViewOpenGL.DrawSliceFilterMarkers(W, H: Integer);
|
||||
const
|
||||
SLICE_CLR = TColor($0010A8F0); // янтарный (BGR), как акцент TX-select флага
|
||||
var
|
||||
i, SX1, SX2, SVfoX: Integer;
|
||||
O: TVfoOverlay;
|
||||
begin
|
||||
for i := 0 to SliceOverlayCount - 1 do
|
||||
begin
|
||||
O := SliceOverlayAt(i);
|
||||
if (O = nil) or (not O.Visible) then Continue;
|
||||
CalcFilterBandXFor(O.DisplayVfoHz, O.DisplayMode, O.DisplayBW, W, SX1, SX2, SVfoX);
|
||||
if SX2 > SX1 then DrawRect(SX1, 0, SX2, H, SLICE_CLR, 60 / 255);
|
||||
DrawLine(SX1, 0, SX1, H, SLICE_CLR, 1, False);
|
||||
DrawLine(SX2, 0, SX2, H, SLICE_CLR, 1, False);
|
||||
DrawLine(SVfoX, 0, SVfoX, H - 12, SLICE_CLR, 2, False);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSpectrumViewOpenGL.DrawAGCLines(W, H: Integer; DBmax,
|
||||
@@ -783,16 +826,11 @@ begin
|
||||
if Assigned(FVfoOverlay) and FVfoOverlay.Visible then
|
||||
begin
|
||||
if FOverlayDirty or FVfoOverlayDirty or FVfoOverlayTex.Dirty or
|
||||
FVfoOverlay.CacheDirty or FVfoOverlay.MeterDirty or
|
||||
(FLastVfoX <> FVfoOverlay.Left) or (FLastVfoY <> FVfoOverlay.Top) or
|
||||
(FLastVfoW <> FVfoOverlay.Width) or (FLastVfoH <> FVfoOverlay.Height) then
|
||||
begin
|
||||
B := TBitmap.Create;
|
||||
try
|
||||
FVfoOverlay.DrawOverlayBitmap(B);
|
||||
UploadBitmap(FVfoOverlayTex, B, True, VFO_OVERLAY_ALPHA);
|
||||
finally
|
||||
B.Free;
|
||||
end;
|
||||
UploadBitmap(FVfoOverlayTex, FVfoOverlay.EnsureRendered, True, VFO_OVERLAY_ALPHA);
|
||||
FLastVfoX := FVfoOverlay.Left;
|
||||
FLastVfoY := FVfoOverlay.Top;
|
||||
FLastVfoW := FVfoOverlay.Width;
|
||||
@@ -801,9 +839,76 @@ begin
|
||||
end;
|
||||
DrawTexture(FVfoOverlayTex, FVfoOverlay.Left, FVfoOverlay.Top);
|
||||
end;
|
||||
|
||||
DrawSliceOverlays(W, H);
|
||||
FOverlayDirty := False;
|
||||
end;
|
||||
|
||||
function TSpectrumViewOpenGL.SliceTexIndex(O: TVfoOverlay): Integer;
|
||||
var i: Integer;
|
||||
begin
|
||||
Result := -1;
|
||||
for i := 0 to High(FSliceTex) do
|
||||
if FSliceTex[i].Overlay = O then Exit(i);
|
||||
end;
|
||||
|
||||
// Флаги слайсов (B+): своя GL-текстура на флаг (привязка по указателю оверлея).
|
||||
// Текстуры «сирот» (флаг удалён) вычищаются здесь же.
|
||||
procedure TSpectrumViewOpenGL.DrawSliceOverlays(W, H: Integer);
|
||||
var
|
||||
n, i, ti, j: Integer;
|
||||
O: TVfoOverlay;
|
||||
Alive: Boolean;
|
||||
begin
|
||||
// 1. Убрать текстуры флагов, которых уже нет в списке.
|
||||
i := 0;
|
||||
while i <= High(FSliceTex) do
|
||||
begin
|
||||
Alive := False;
|
||||
for j := 0 to SliceOverlayCount - 1 do
|
||||
if SliceOverlayAt(j) = FSliceTex[i].Overlay then begin Alive := True; Break; end;
|
||||
if not Alive then
|
||||
begin
|
||||
DeleteTexture(FSliceTex[i].Tex);
|
||||
for j := i to High(FSliceTex) - 1 do FSliceTex[j] := FSliceTex[j + 1];
|
||||
SetLength(FSliceTex, Length(FSliceTex) - 1);
|
||||
end
|
||||
else
|
||||
Inc(i);
|
||||
end;
|
||||
|
||||
// 2. Нарисовать каждый видимый флаг, при нужде (пере)залив текстуру.
|
||||
n := SliceOverlayCount;
|
||||
for i := 0 to n - 1 do
|
||||
begin
|
||||
O := SliceOverlayAt(i);
|
||||
if (O = nil) or (not O.Visible) then Continue;
|
||||
ti := SliceTexIndex(O);
|
||||
if ti < 0 then
|
||||
begin
|
||||
SetLength(FSliceTex, Length(FSliceTex) + 1);
|
||||
ti := High(FSliceTex);
|
||||
FSliceTex[ti].Overlay := O;
|
||||
FSliceTex[ti].Tex.Dirty := True;
|
||||
FSliceTex[ti].LastX := MaxInt; // форсируем первую заливку
|
||||
end;
|
||||
// Перезаливаем текстуру слайса ТОЛЬКО когда изменился он сам (CacheDirty:
|
||||
// режим/громкость, MeterDirty: S-метр) или его позиция — не по глобальному
|
||||
// FOverlayDirty. При MeterDirty EnsureRendered перерисует лишь полоску метра.
|
||||
if O.CacheDirty or O.MeterDirty or FSliceTex[ti].Tex.Dirty or
|
||||
(FSliceTex[ti].LastX <> O.Left) or (FSliceTex[ti].LastY <> O.Top) or
|
||||
(FSliceTex[ti].LastW <> O.Width) or (FSliceTex[ti].LastH <> O.Height) then
|
||||
begin
|
||||
UploadBitmap(FSliceTex[ti].Tex, O.EnsureRendered, True, VFO_OVERLAY_ALPHA);
|
||||
FSliceTex[ti].LastX := O.Left;
|
||||
FSliceTex[ti].LastY := O.Top;
|
||||
FSliceTex[ti].LastW := O.Width;
|
||||
FSliceTex[ti].LastH := O.Height;
|
||||
end;
|
||||
DrawTexture(FSliceTex[ti].Tex, O.Left, O.Top);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSpectrumViewOpenGL.PaintSpectrum(Sender: TObject);
|
||||
var
|
||||
W, H: Integer;
|
||||
|
||||
Reference in New Issue
Block a user