mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
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:
co-authored by
Claude Opus 4.8
parent
33c433ce5e
commit
8be98b72e1
+10
-25
@@ -989,8 +989,7 @@ begin
|
|||||||
TSettingsManager.DefaultBand(bi_, FController.FBandCache[bi_]);
|
TSettingsManager.DefaultBand(bi_, FController.FBandCache[bi_]);
|
||||||
FController.FCurrentBand := 5;
|
FController.FCurrentBand := 5;
|
||||||
|
|
||||||
// Загружаем JSON настройки
|
// Загружаем JSON настройки (FSettings создаётся в TRadioController.Create)
|
||||||
FController.FSettings := TSettingsManager.Create;
|
|
||||||
FController.FSettings.Load;
|
FController.FSettings.Load;
|
||||||
|
|
||||||
// Загружаем список каналов
|
// Загружаем список каналов
|
||||||
@@ -1152,7 +1151,10 @@ begin
|
|||||||
FSpecView.PAMaxPower := FController.FPAMaxPower;
|
FSpecView.PAMaxPower := FController.FPAMaxPower;
|
||||||
FWidebandView := TWidebandView.Create;
|
FWidebandView := TWidebandView.Create;
|
||||||
|
|
||||||
FController.FNetwork := THPSDRNetwork.Create;
|
// Движки создаёт контроллер (владелец lifecycle — нужно для демона); здесь
|
||||||
|
// лишь навешиваем колбэки на методы MainForm. FSampleRate уже загружен из
|
||||||
|
// startup-preview выше, поэтому FDSPEngine рождается с нужной частотой.
|
||||||
|
FController.CreateEngines(FController.FSampleRate);
|
||||||
FController.FNetwork.OnDeviceFound := OnDeviceFound;
|
FController.FNetwork.OnDeviceFound := OnDeviceFound;
|
||||||
FController.FNetwork.OnHPStatus := OnHPStatusCB;
|
FController.FNetwork.OnHPStatus := OnHPStatusCB;
|
||||||
FController.FNetwork.OnDDCIQ := OnDDCIQCB;
|
FController.FNetwork.OnDDCIQ := OnDDCIQCB;
|
||||||
@@ -1170,13 +1172,8 @@ begin
|
|||||||
UpdateVfoDisplay;
|
UpdateVfoDisplay;
|
||||||
UpdateFilterButtons;
|
UpdateFilterButtons;
|
||||||
|
|
||||||
// DSP Engine — создаём объект, Open вызовется при нажатии START
|
// DSP Engine.Open (загрузка libwdsp, ~1-2 сек) вызывается при нажатии START.
|
||||||
// (FController.FDSPEngine.Open загружает libwdsp и занимает ~1-2 сек)
|
|
||||||
FController.FWDSPReady := False;
|
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.OnAudio := OnAudioReady;
|
||||||
FController.FDSPEngine.OnSpectrum := OnSpectrumReady;
|
FController.FDSPEngine.OnSpectrum := OnSpectrumReady;
|
||||||
FController.FDSPEngine.OnWaterfall := OnWaterfallReady;
|
FController.FDSPEngine.OnWaterfall := OnWaterfallReady;
|
||||||
@@ -1185,12 +1182,6 @@ begin
|
|||||||
// тиком, если выбран источник SoundCard. Для Radio колбэк не вызывается.
|
// тиком, если выбран источник SoundCard. Для Radio колбэк не вызывается.
|
||||||
FController.FDSPEngine.OnPullMicSamples := PullSoundCardMic;
|
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 := TTimer.Create(Self);
|
||||||
FMeterTimer.Interval := 100;
|
FMeterTimer.Interval := 100;
|
||||||
FMeterTimer.OnTimer := MeterTimerTick;
|
FMeterTimer.OnTimer := MeterTimerTick;
|
||||||
@@ -1221,16 +1212,11 @@ procedure TMainForm.FormDestroy(Sender: TObject);
|
|||||||
begin
|
begin
|
||||||
FMeterTimer.Enabled := False;
|
FMeterTimer.Enabled := False;
|
||||||
FSpectrumTimer.Enabled := False;
|
FSpectrumTimer.Enabled := False;
|
||||||
|
// Грациозный стоп радио (Run=0) до освобождения; сами движки закрывает и
|
||||||
|
// освобождает TRadioController.Destroy (FreeEngines) ниже.
|
||||||
if FController.FNetwork.Running then
|
if FController.FNetwork.Running then
|
||||||
FController.FNetwork.SetRunAndFreq(False, XvtrTranslate(FController.FCenterFreq), XvtrTranslate(FController.FCenterFreq), 0);
|
FController.FNetwork.SetRunAndFreq(False, XvtrTranslate(FController.FCenterFreq), XvtrTranslate(FController.FCenterFreq), 0);
|
||||||
FController.FNetwork.Disconnect;
|
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(FSpecView);
|
||||||
FreeAndNil(FWidebandView);
|
FreeAndNil(FWidebandView);
|
||||||
|
|
||||||
@@ -1245,15 +1231,14 @@ begin
|
|||||||
// Размер окна сохраняем всегда (не зависит от подключения)
|
// Размер окна сохраняем всегда (не зависит от подключения)
|
||||||
FController.FSettings.SaveStartupPreview(FController.FVfoA, FController.FVfoB, FController.FSampleRate);
|
FController.FSettings.SaveStartupPreview(FController.FVfoA, FController.FVfoB, FController.FSampleRate);
|
||||||
FController.FSettings.Save;
|
FController.FSettings.Save;
|
||||||
FController.FSettings.Free;
|
|
||||||
FreeAndNil(FController.FChannelStore);
|
FreeAndNil(FController.FChannelStore);
|
||||||
FWebServer.Stop;
|
FWebServer.Stop;
|
||||||
FWebServer.Free;
|
FWebServer.Free;
|
||||||
if Assigned(FCATTcp) then begin FCATTcp.Stop; FreeAndNil(FCATTcp); end;
|
if Assigned(FCATTcp) then begin FCATTcp.Stop; FreeAndNil(FCATTcp); end;
|
||||||
if Assigned(FCATSerial) then begin FCATSerial.StopAll; FreeAndNil(FCATSerial); end;
|
if Assigned(FCATSerial) then begin FCATSerial.StopAll; FreeAndNil(FCATSerial); end;
|
||||||
FreeAndNil(FCATEngine);
|
FreeAndNil(FCATEngine);
|
||||||
// Движки (поля FController) уже освобождены выше.
|
// Ядро освобождаем последним — его Destroy закрывает и освобождает движки
|
||||||
// Освобождаем само ядро последним.
|
// (FreeEngines) и FSettings.
|
||||||
FreeAndNil(FController);
|
FreeAndNil(FController);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
+39
-1
@@ -203,6 +203,13 @@ type
|
|||||||
constructor Create;
|
constructor Create;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
|
||||||
|
// Lifecycle движков: создаёт FNetwork/FDSPEngine/FAudioOut/FAudioIn с
|
||||||
|
// заданным sample rate (вызывающий уже определил его из настроек). Колбэки
|
||||||
|
// движков навешивает вызывающий (GUI: методы MainForm; демон: свои). Парный
|
||||||
|
// FreeEngines закрывает и освобождает их; вызывается из Destroy.
|
||||||
|
procedure CreateEngines(ASampleRate: Integer);
|
||||||
|
procedure FreeEngines;
|
||||||
|
|
||||||
// Маршалинг команды в поток контроллера (GUI: TThread.Synchronize).
|
// Маршалинг команды в поток контроллера (GUI: TThread.Synchronize).
|
||||||
procedure Invoke(M: TThreadMethod);
|
procedure Invoke(M: TThreadMethod);
|
||||||
|
|
||||||
@@ -343,6 +350,7 @@ implementation
|
|||||||
constructor TRadioController.Create;
|
constructor TRadioController.Create;
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
|
FSettings := TSettingsManager.Create; // владелец настроек (GUI и демон)
|
||||||
// Дефолты (дублируют TMainForm.FormCreate; в GUI перезапишутся, в демоне нужны).
|
// Дефолты (дублируют TMainForm.FormCreate; в GUI перезапишутся, в демоне нужны).
|
||||||
FVfoA := 14200000; FVfoB := 7100000; FActiveVfo := 0;
|
FVfoA := 14200000; FVfoB := 7100000; FActiveVfo := 0;
|
||||||
FMode := 1; FFilter := 5; FFilterBW := 2700;
|
FMode := 1; FFilter := 5; FFilterBW := 2700;
|
||||||
@@ -357,9 +365,39 @@ begin
|
|||||||
FillChar(FDevMAC, SizeOf(FDevMAC), 0);
|
FillChar(FDevMAC, SizeOf(FDevMAC), 0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TRadioController.CreateEngines(ASampleRate: Integer);
|
||||||
|
// Создаёт движки в полях контроллера. Колбэки (OnAudio/OnSpectrum/OnHPStatus…)
|
||||||
|
// навешивает вызывающий после возврата — они указывают на UI/демон.
|
||||||
|
begin
|
||||||
|
FNetwork := THPSDRNetwork.Create;
|
||||||
|
|
||||||
|
// BufSize=512: латентность 512/48000 ≈ 10.7мс — допустимо для SDR; меньший
|
||||||
|
// буфер вдвое сокращает построение downsampler в OpenChannel RXA.
|
||||||
|
FDSPEngine := TWDSPEngine.Create(ASampleRate, 48000, 512);
|
||||||
|
|
||||||
|
// Audio out/in — объекты создаём сейчас, Open вызывается позже (после показа
|
||||||
|
// формы / при подключении устройства).
|
||||||
|
FAudioOut := TAudioOutput.Create(48000);
|
||||||
|
FAudioOut.OutputBufferSize := FSettings.LoadAudioBufferSize;
|
||||||
|
FAudioIn := TAudioInput.Create(48000);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TRadioController.FreeEngines;
|
||||||
|
begin
|
||||||
|
if Assigned(FNetwork) then
|
||||||
|
begin
|
||||||
|
if FNetwork.Connected then FNetwork.Disconnect;
|
||||||
|
FreeAndNil(FNetwork);
|
||||||
|
end;
|
||||||
|
if Assigned(FAudioOut) then begin FAudioOut.Close; FreeAndNil(FAudioOut); end;
|
||||||
|
if Assigned(FAudioIn) then begin FAudioIn.Close; FreeAndNil(FAudioIn); end;
|
||||||
|
if Assigned(FDSPEngine) then begin FDSPEngine.Close; FreeAndNil(FDSPEngine); end;
|
||||||
|
end;
|
||||||
|
|
||||||
destructor TRadioController.Destroy;
|
destructor TRadioController.Destroy;
|
||||||
begin
|
begin
|
||||||
// Фаза 1: движки освобождает MainForm. Здесь ничего не освобождаем.
|
FreeEngines;
|
||||||
|
FreeAndNil(FSettings);
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user