Phase 5 (batch 22): move engine lifecycle into the controller

TRadioController now owns its engines: CreateEngines(ASampleRate) builds
FNetwork/FDSPEngine/FAudioOut/FAudioIn (and the constructor now owns
FSettings), FreeEngines closes and frees them, and Destroy calls
FreeEngines + frees FSettings. FormCreate replaces the four inline
T...Create calls with a single FController.CreateEngines (after the
startup sample rate is loaded, so FDSPEngine is still born at the right
rate) and keeps wiring the MainForm callbacks. FormDestroy keeps the
graceful Run=0 stop + Disconnect but drops the per-engine Close/Free and
FSettings.Free, which now happen in the controller's destructor. This is
a behaviour-preserving ownership move toward headless/daemon operation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Uladzimir Karpenka
2026-06-05 17:20:17 +03:00
co-authored by Claude Opus 4.8
parent 33c433ce5e
commit 8be98b72e1
2 changed files with 49 additions and 26 deletions
+10 -25
View File
@@ -989,8 +989,7 @@ begin
TSettingsManager.DefaultBand(bi_, FController.FBandCache[bi_]);
FController.FCurrentBand := 5;
// Загружаем JSON настройки
FController.FSettings := TSettingsManager.Create;
// Загружаем JSON настройки (FSettings создаётся в TRadioController.Create)
FController.FSettings.Load;
// Загружаем список каналов
@@ -1152,7 +1151,10 @@ begin
FSpecView.PAMaxPower := FController.FPAMaxPower;
FWidebandView := TWidebandView.Create;
FController.FNetwork := THPSDRNetwork.Create;
// Движки создаёт контроллер (владелец lifecycle — нужно для демона); здесь
// лишь навешиваем колбэки на методы MainForm. FSampleRate уже загружен из
// startup-preview выше, поэтому FDSPEngine рождается с нужной частотой.
FController.CreateEngines(FController.FSampleRate);
FController.FNetwork.OnDeviceFound := OnDeviceFound;
FController.FNetwork.OnHPStatus := OnHPStatusCB;
FController.FNetwork.OnDDCIQ := OnDDCIQCB;
@@ -1170,13 +1172,8 @@ begin
UpdateVfoDisplay;
UpdateFilterButtons;
// DSP Engine — создаём объект, Open вызовется при нажатии START
// (FController.FDSPEngine.Open загружает libwdsp и занимает ~1-2 сек)
// DSP Engine.Open (загрузка libwdsp, ~1-2 сек) вызывается при нажатии START.
FController.FWDSPReady := False;
// BufSize=512: FBufSize=2048 @ 192kHz, FAudioBufSize=512 @ 48kHz.
// Уменьшение с 1024 вдвое сокращает время построения downsampler в OpenChannel RXA
// (~1300ms → ~650ms). Латентность: 512/48000 ≈ 10.7ms — допустимо для SDR.
FController.FDSPEngine := TWDSPEngine.Create(FController.FSampleRate, 48000, 512);
FController.FDSPEngine.OnAudio := OnAudioReady;
FController.FDSPEngine.OnSpectrum := OnSpectrumReady;
FController.FDSPEngine.OnWaterfall := OnWaterfallReady;
@@ -1185,12 +1182,6 @@ begin
// тиком, если выбран источник SoundCard. Для Radio колбэк не вызывается.
FController.FDSPEngine.OnPullMicSamples := PullSoundCardMic;
// Audio output/input — создаём объекты сейчас, открываем после показа формы
// (Pa_Initialize на Linux пишет в stderr до перехвата сигналов FPC)
FController.FAudioOut := TAudioOutput.Create(48000);
FController.FAudioOut.OutputBufferSize := FController.FSettings.LoadAudioBufferSize;
FController.FAudioIn := TAudioInput.Create(48000);
FMeterTimer := TTimer.Create(Self);
FMeterTimer.Interval := 100;
FMeterTimer.OnTimer := MeterTimerTick;
@@ -1221,16 +1212,11 @@ procedure TMainForm.FormDestroy(Sender: TObject);
begin
FMeterTimer.Enabled := False;
FSpectrumTimer.Enabled := False;
// Грациозный стоп радио (Run=0) до освобождения; сами движки закрывает и
// освобождает TRadioController.Destroy (FreeEngines) ниже.
if FController.FNetwork.Running then
FController.FNetwork.SetRunAndFreq(False, XvtrTranslate(FController.FCenterFreq), XvtrTranslate(FController.FCenterFreq), 0);
FController.FNetwork.Disconnect;
FController.FNetwork.Free;
FController.FAudioOut.Close;
FController.FAudioOut.Free;
FController.FAudioIn.Close;
FController.FAudioIn.Free;
FController.FDSPEngine.Close;
FController.FDSPEngine.Free;
FreeAndNil(FSpecView);
FreeAndNil(FWidebandView);
@@ -1245,15 +1231,14 @@ begin
// Размер окна сохраняем всегда (не зависит от подключения)
FController.FSettings.SaveStartupPreview(FController.FVfoA, FController.FVfoB, FController.FSampleRate);
FController.FSettings.Save;
FController.FSettings.Free;
FreeAndNil(FController.FChannelStore);
FWebServer.Stop;
FWebServer.Free;
if Assigned(FCATTcp) then begin FCATTcp.Stop; FreeAndNil(FCATTcp); end;
if Assigned(FCATSerial) then begin FCATSerial.StopAll; FreeAndNil(FCATSerial); end;
FreeAndNil(FCATEngine);
// Движки (поля FController) уже освобождены выше.
// Освобождаем само ядро последним.
// Ядро освобождаем последним — его Destroy закрывает и освобождает движки
// (FreeEngines) и FSettings.
FreeAndNil(FController);
end;