From b3e4360a5dd91144755f36e18e1305e95867fb01 Mon Sep 17 00:00:00 2001 From: Uladzimir Karpenka Date: Thu, 4 Jun 2026 14:58:03 +0300 Subject: [PATCH] Phase 3 (batch 3): route Volume, AGC-top, WfAGC/WfNF through controller First sliders through the controller, exercising the FSyncingFromController re-entrancy guard: - SetVolume/SetAGCTop clamp, apply to the DSP engine, update band cache, and fire OnStateChanged. SetAGCTop reuses ApplyAGCToEngine (the unified AGC index->enum mapping), fixing the same bad TWDSPAGCMode(index) cast that TrkAGCChange/SyncWebAGCTop used when re-applying AGC on level change. - OnControllerState updates the slider/label/spectrum (rfAGCTop also refreshes the FSpecView AGC line, now consistent for web-initiated changes too) and the FSpecView waterfall flags (rfWfAGC/rfWfNF). - TrkVolumeChange/TrkAGCChange (guarded), SyncWebVolume/AGCTop/WfAGC/WfNF, and ApplyWfAGCNF all call the commands. Builds clean (cocoa). Co-Authored-By: Claude Opus 4.8 --- MainForm.pas | 58 +++++++++++++++++++-------------------------- RadioController.pas | 29 +++++++++++++++++++---- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/MainForm.pas b/MainForm.pas index 77339dc..94fbe3f 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -1439,6 +1439,20 @@ begin rfANF: UpdateANFButton; rfAGCMode: for i := 0 to 4 do StyleButton(BtnAGCMode[i], i = FController.FAGCMode); + rfVolume: + TrkVolume.Position := FController.FVolume; + rfAGCTop: + begin + TrkAGC.Position := FController.FAGCTop; + LblAGCTop.Caption := Format('%ddB', [FController.FAGCTop]); + FSpecView.AGCTop := FController.FAGCTop; + FSpecView.DrawSpectrum; + PbSpectrum.Invalidate; + end; + rfWfAGC: + FSpecView.WfAGCEnabled := FController.FWfAGCEnabled; + rfWfNF: + FSpecView.WfNFEnabled := FController.FWfNFEnabled; end; finally FSyncingFromController := False; @@ -5945,10 +5959,8 @@ end; procedure TMainForm.ApplyWfAGCNF(WfAGC, WfNF: Boolean); begin - FController.FWfAGCEnabled := WfAGC; - FController.FWfNFEnabled := WfNF; - FSpecView.WfAGCEnabled := FController.FWfAGCEnabled; - FSpecView.WfNFEnabled := FController.FWfNFEnabled; + FController.SetWfAGC(WfAGC); + FController.SetWfNF(WfNF); end; procedure TMainForm.ApplyADCSettings(Dither, Random: Boolean); @@ -6301,15 +6313,7 @@ end; procedure TMainForm.SyncWebAGCTop; begin - FController.FAGCTop := Max(20, Min(120, FWebSyncInt)); - TrkAGC.Position := FController.FAGCTop; - LblAGCTop.Caption := Format('%ddB', [FController.FAGCTop]); - if FController.FWDSPReady then - begin - FController.FDSPEngine.SetAGCTop(FController.FAGCTop); - FController.FDSPEngine.SetAGC(TWDSPAGCMode(FController.FAGCMode), 50.0); - end; - FController.FBandCache[FController.FCurrentBand].AGCTop := FController.FAGCTop; + FController.SetAGCTop(FWebSyncInt); // клампит, применяет, OnControllerState обновит UI end; procedure TMainForm.WebOnBand(Idx: Integer); @@ -6386,9 +6390,7 @@ end; procedure TMainForm.SyncWebVolume; begin - FController.FVolume := Max(0, Min(100, FWebSyncInt)); - TrkVolume.Position := FController.FVolume; - if FController.FWDSPReady then FController.FDSPEngine.SetVolume(FController.FVolume / 100.0); + FController.SetVolume(FWebSyncInt); end; procedure TMainForm.WebOnWfAGC(On_: Boolean); @@ -6400,8 +6402,7 @@ end; procedure TMainForm.SyncWebWfAGC; begin - FController.FWfAGCEnabled := FWebSyncBool; - FSpecView.WfAGCEnabled := FController.FWfAGCEnabled; + FController.SetWfAGC(FWebSyncBool); end; procedure TMainForm.WebOnWfNF(On_: Boolean); @@ -6413,8 +6414,7 @@ end; procedure TMainForm.SyncWebWfNF; begin - FController.FWfNFEnabled := FWebSyncBool; - FSpecView.WfNFEnabled := FController.FWfNFEnabled; + FController.SetWfNF(FWebSyncBool); end; // ── Run ──────────────────────────────────────────────────────────────────── @@ -7229,9 +7229,8 @@ end; procedure TMainForm.TrkVolumeChange(Sender: TObject); begin - FController.FVolume := TrkVolume.Position; - if FController.FWDSPReady then - FController.FDSPEngine.SetVolume(FController.FVolume / 100.0); + if FSyncingFromController then Exit; + FController.SetVolume(TrkVolume.Position); end; // =========================================================================== @@ -7293,17 +7292,8 @@ end; procedure TMainForm.TrkAGCChange(Sender: TObject); begin - FController.FAGCTop := TrkAGC.Position; - LblAGCTop.Caption := Format('%ddB', [FController.FAGCTop]); - if FController.FWDSPReady then - begin - FController.FDSPEngine.SetAGCTop(FController.FAGCTop); - FController.FDSPEngine.SetAGC(TWDSPAGCMode(FController.FAGCMode), 50.0); - end; - FController.FBandCache[FController.FCurrentBand].AGCTop := FController.FAGCTop; - FSpecView.AGCTop := FController.FAGCTop; - FSpecView.DrawSpectrum; - PbSpectrum.Invalidate; + if FSyncingFromController then Exit; + FController.SetAGCTop(TrkAGC.Position); end; // =========================================================================== diff --git a/RadioController.pas b/RadioController.pas index 44ac962..03076f4 100644 --- a/RadioController.pas +++ b/RadioController.pas @@ -108,6 +108,7 @@ type FOnInvoke: TUIInvokeEvent; FOnStateChanged: TRadioStateEvent; procedure Changed(Field: TRadioField); + procedure ApplyAGCToEngine; // применяет FAGCMode к WDSP (единый маппинг) public // ============================================================ // ПУБЛИЧНЫЕ ПОЛЯ (миграционные — имена совпадают с TMainForm). @@ -381,27 +382,45 @@ begin FFilter := Idx; Changed(rfFilter); { TODO wiring } end; procedure TRadioController.SetFilterBW(BWHz: Integer); begin FFilterBW := BWHz; Changed(rfFilterBW); { TODO wiring } end; -procedure TRadioController.SetAGCMode(M: Integer); +procedure TRadioController.ApplyAGCToEngine; const // UI-индекс 0..4 (FAST/MED/SLOW/LONG/OFF) -> WDSP enum. Единый маппинг для - // всех фронтендов (раньше web ошибочно кастовал индекс прямо в enum). + // всех фронтендов (раньше web/слайдер ошибочно кастовали индекс прямо в enum). AGCModes: array[0..4] of TWDSPAGCMode = (agcFast, agcMedium, agcSlow, agcLong, agcOff); +begin + if FWDSPReady and Assigned(FDSPEngine) and (FAGCMode >= 0) and (FAGCMode <= 4) then + FDSPEngine.SetAGC(AGCModes[FAGCMode], 50.0); +end; + +procedure TRadioController.SetAGCMode(M: Integer); begin if (M < 0) or (M > 4) then Exit; FAGCMode := M; - if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetAGC(AGCModes[M], 50.0); + ApplyAGCToEngine; FBandCache[FCurrentBand].AGCMode := FAGCMode; Changed(rfAGCMode); end; procedure TRadioController.SetAGCTop(DB: Integer); -begin FAGCTop := DB; Changed(rfAGCTop); { TODO wiring } end; +begin + if DB < 20 then DB := 20 else if DB > 120 then DB := 120; + FAGCTop := DB; + if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetAGCTop(FAGCTop); + ApplyAGCToEngine; // ре-применяем режим с новым уровнем + FBandCache[FCurrentBand].AGCTop := FAGCTop; + Changed(rfAGCTop); +end; procedure TRadioController.AGCTopBy(Delta: Integer); begin SetAGCTop(FAGCTop + Delta); end; procedure TRadioController.SetVolume(V: Integer); -begin FVolume := V; Changed(rfVolume); { TODO wiring: FDSPEngine.SetVolume } end; +begin + if V < 0 then V := 0 else if V > 100 then V := 100; + FVolume := V; + if FWDSPReady and Assigned(FDSPEngine) then FDSPEngine.SetVolume(FVolume / 100.0); + Changed(rfVolume); +end; procedure TRadioController.VolumeBy(Delta: Integer); begin SetVolume(FVolume + Delta); end;