Phase 5 (batch 23): move TX mic callbacks into the controller

OnMicPacket (HW mic packet -> WDSP, only when TX mic source is Radio) and
PullSoundCardMic (sound-card input -> WDSP, when source is SoundCard) are
pure data-path handlers with no UI/MainForm state, so they move verbatim
into TRadioController. FormCreate now wires FNetwork.OnMicPacket and
FDSPEngine.OnPullMicSamples to the controller's own methods. These are
backend-agnostic consumers (they operate on the common interchange
format), so they belong in the core regardless of HPSDR vs future Pluto
backends. Behaviour unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Uladzimir Karpenka
2026-06-05 17:33:11 +03:00
co-authored by Claude Opus 4.8
parent 8be98b72e1
commit 3d2d76452c
2 changed files with 37 additions and 35 deletions
+2 -35
View File
@@ -375,7 +375,6 @@ type
Count: Integer); Count: Integer);
procedure OnWDSPOpenDone(Success: Boolean); procedure OnWDSPOpenDone(Success: Boolean);
procedure DoConnectDevice(const Dev: THPSDRDevice); procedure DoConnectDevice(const Dev: THPSDRDevice);
procedure OnMicPacketCB(const Data: TMicDataPacket);
// DSP/Audio callbacks (вызываются из рабочих потоков) // DSP/Audio callbacks (вызываются из рабочих потоков)
procedure OnAudioReady(const Left, Right: array of Single; Count: Integer); procedure OnAudioReady(const Left, Right: array of Single; Count: Integer);
@@ -640,7 +639,6 @@ type
// (mic-биты Boost/Bias/LineIn/PTT попадают в byte 50 DUCSpecific). // (mic-биты Boost/Bias/LineIn/PTT попадают в byte 50 DUCSpecific).
procedure ApplyTXSettingsToDSP; procedure ApplyTXSettingsToDSP;
procedure SendDUCSpecificFromSettings; procedure SendDUCSpecificFromSettings;
function PullSoundCardMic(MaxN: Integer): Integer;
function BuildMicLineSelectByte: Byte; function BuildMicLineSelectByte: Byte;
procedure ApplyVisibility(ShowSpectrum, ShowWaterfall, ShowWideband, procedure ApplyVisibility(ShowSpectrum, ShowWaterfall, ShowWideband,
WidebandFill, SpectrumFill: Boolean); WidebandFill, SpectrumFill: Boolean);
@@ -1158,7 +1156,7 @@ begin
FController.FNetwork.OnDeviceFound := OnDeviceFound; FController.FNetwork.OnDeviceFound := OnDeviceFound;
FController.FNetwork.OnHPStatus := OnHPStatusCB; FController.FNetwork.OnHPStatus := OnHPStatusCB;
FController.FNetwork.OnDDCIQ := OnDDCIQCB; FController.FNetwork.OnDDCIQ := OnDDCIQCB;
FController.FNetwork.OnMicPacket := OnMicPacketCB; FController.FNetwork.OnMicPacket := FController.OnMicPacket;
FController.FNetwork.OnWideband := OnWidebandCB; FController.FNetwork.OnWideband := OnWidebandCB;
// Дефолты TX, Alex и XVTR (на случай если устройство ещё не выбрано). // Дефолты TX, Alex и XVTR (на случай если устройство ещё не выбрано).
TSettingsManager.DefaultTX(FController.FTXSettings); TSettingsManager.DefaultTX(FController.FTXSettings);
@@ -1180,7 +1178,7 @@ begin
FController.FDSPEngine.OnTXIQ := OnTXIQReady; FController.FDSPEngine.OnTXIQ := OnTXIQReady;
// Sound-card mic путь: WDSP TX-thread дёргает этот колбэк перед каждым // Sound-card mic путь: WDSP TX-thread дёргает этот колбэк перед каждым
// тиком, если выбран источник SoundCard. Для Radio колбэк не вызывается. // тиком, если выбран источник SoundCard. Для Radio колбэк не вызывается.
FController.FDSPEngine.OnPullMicSamples := PullSoundCardMic; FController.FDSPEngine.OnPullMicSamples := FController.PullSoundCardMic;
FMeterTimer := TTimer.Create(Self); FMeterTimer := TTimer.Create(Self);
FMeterTimer.Interval := 100; FMeterTimer.Interval := 100;
@@ -3565,15 +3563,6 @@ begin
SetStatusText(4, 'RX running'); SetStatusText(4, 'RX running');
end; end;
procedure TMainForm.OnMicPacketCB(const Data: TMicDataPacket);
begin
// HW mic-стрим уходит в WDSP только если выбран источник Radio.
// Иначе сэмплы игнорируются (mic берётся со звуковой карты).
if FController.FTransmitting and FController.FWDSPReady and
(FController.FDSPEngine.TXMicSource = txmsRadio) then
FController.FDSPEngine.PushTXMicSamples16(Data.Samples, 64);
end;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// TX settings — применение к WDSP, сборка DUC Specific, sound-card pull // TX settings — применение к WDSP, сборка DUC Specific, sound-card pull
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -3670,28 +3659,6 @@ begin
FController.FTXSettings.TXWfAvgTimeMS); FController.FTXSettings.TXWfAvgTimeMS);
end; end;
function TMainForm.PullSoundCardMic(MaxN: Integer): Integer;
// Колбэк, который TTXDSPThread зовёт при TXMicSource=txmsSoundCard.
// Тянем только реально доступные сэмплы из FController.FAudioIn — иначе мы пушили бы
// нули и mic-цепь в WDSP получала бы тишину вместо реального сигнала.
// Читаем пачкой в локальный буфер и пушим одним вызовом — экономим
// будилки семафора и предотвращаем подмену TX-блока тишиной.
var
i, n: Integer;
Buf: array[0..2047] of Double;
begin
Result := 0;
if (FController.FAudioIn = nil) or (not FController.FAudioIn.IsOpen) then Exit;
n := FController.FAudioIn.Available;
if n > MaxN then n := MaxN;
if n > Length(Buf) then n := Length(Buf);
for i := 0 to n - 1 do
Buf[i] := FController.FAudioIn.ReadSample;
if n > 0 then
FController.FDSPEngine.PushTXMicSamplesD(Buf, n);
Result := n;
end;
// =========================================================================== // ===========================================================================
// Button handlers // Button handlers
// =========================================================================== // ===========================================================================
+35
View File
@@ -210,6 +210,12 @@ type
procedure CreateEngines(ASampleRate: Integer); procedure CreateEngines(ASampleRate: Integer);
procedure FreeEngines; procedure FreeEngines;
// ---- Колбэки движков (вызываются из рабочих потоков сети/DSP) ----
// Чистая обработка данных без UI; навешиваются на FNetwork/FDSPEngine
// вызывающим (GUI или демон) после CreateEngines.
procedure OnMicPacket(const Data: TMicDataPacket);
function PullSoundCardMic(MaxN: Integer): Integer;
// Маршалинг команды в поток контроллера (GUI: TThread.Synchronize). // Маршалинг команды в поток контроллера (GUI: TThread.Synchronize).
procedure Invoke(M: TThreadMethod); procedure Invoke(M: TThreadMethod);
@@ -418,6 +424,35 @@ begin
end; end;
end; end;
procedure TRadioController.OnMicPacket(const Data: TMicDataPacket);
// HW mic-стрим уходит в WDSP только если выбран источник Radio; иначе сэмплы
// игнорируются (mic берётся со звуковой карты).
begin
if FTransmitting and FWDSPReady and
(FDSPEngine.TXMicSource = txmsRadio) then
FDSPEngine.PushTXMicSamples16(Data.Samples, 64);
end;
function TRadioController.PullSoundCardMic(MaxN: Integer): Integer;
// Колбэк TTXDSPThread при TXMicSource=txmsSoundCard. Тянем только реально
// доступные сэмплы из FAudioIn (иначе пушили бы тишину в mic-цепь WDSP),
// пачкой в локальный буфер — экономим будилки семафора.
var
i, n: Integer;
Buf: array[0..2047] of Double;
begin
Result := 0;
if (FAudioIn = nil) or (not FAudioIn.IsOpen) then Exit;
n := FAudioIn.Available;
if n > MaxN then n := MaxN;
if n > Length(Buf) then n := Length(Buf);
for i := 0 to n - 1 do
Buf[i] := FAudioIn.ReadSample;
if n > 0 then
FDSPEngine.PushTXMicSamplesD(Buf, n);
Result := n;
end;
procedure TRadioController.Invoke(M: TThreadMethod); procedure TRadioController.Invoke(M: TThreadMethod);
begin begin
if Assigned(FOnInvoke) then FOnInvoke(M) if Assigned(FOnInvoke) then FOnInvoke(M)