mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-27 01:17:32 +00:00
Phase 5 (D1): move RX audio routing into the controller
OnAudioReady moves from MainForm into TRadioController (radio-speaker push + local AudioOut). The web steal becomes a route hook: controller fires OnAudioConsume; TWebAdapter.AudioConsume mixes to mono and pushes to the web client, returning True to skip local AudioOut. Behavior-preserving; removes the last audio-path coupling to MainForm. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
682025e99b
commit
7c8fd298ae
+4
-22
@@ -336,7 +336,6 @@ type
|
||||
procedure DoStopAndDisconnect;
|
||||
|
||||
// DSP/Audio callbacks (вызываются из рабочих потоков)
|
||||
procedure OnAudioReady(const Left, Right: array of Single; Count: Integer);
|
||||
procedure OnSpectrumReady(const Pixels: array of Single; Count: Integer);
|
||||
procedure OnWaterfallReady(const Pixels: array of Single; Count: Integer);
|
||||
|
||||
@@ -982,10 +981,12 @@ begin
|
||||
|
||||
// DSP Engine.Open (загрузка libwdsp, ~1-2 сек) вызывается при нажатии START.
|
||||
FController.FWDSPReady := False;
|
||||
FController.FDSPEngine.OnAudio := OnAudioReady;
|
||||
FController.FDSPEngine.OnAudio := FController.OnAudioReady;
|
||||
FController.FDSPEngine.OnSpectrum := OnSpectrumReady;
|
||||
FController.FDSPEngine.OnWaterfall := OnWaterfallReady;
|
||||
FController.FDSPEngine.OnTXIQ := FController.OnTXIQ;
|
||||
// Web-клиент перехватывает RX-аудио (моно-микс) у контроллера, если подключён.
|
||||
FController.OnAudioConsume := FWebAdapter.AudioConsume;
|
||||
// Sound-card mic путь: WDSP TX-thread дёргает этот колбэк перед каждым
|
||||
// тиком, если выбран источник SoundCard. Для Radio колбэк не вызывается.
|
||||
FController.FDSPEngine.OnPullMicSamples := FController.PullSoundCardMic;
|
||||
@@ -3206,7 +3207,7 @@ begin
|
||||
end;
|
||||
FController.FWDSPReady := False;
|
||||
FController.FDSPEngine := TWDSPEngine.Create(ASampleRate, 48000, 512);
|
||||
FController.FDSPEngine.OnAudio := OnAudioReady;
|
||||
FController.FDSPEngine.OnAudio := FController.OnAudioReady;
|
||||
FController.FDSPEngine.OnSpectrum := OnSpectrumReady;
|
||||
FController.FDSPEngine.OnWaterfall := OnWaterfallReady;
|
||||
FController.FDSPEngine.OnTXIQ := FController.OnTXIQ;
|
||||
@@ -5179,25 +5180,6 @@ end;
|
||||
// DSP/Audio callbacks — вызываются из рабочих потоков
|
||||
// ===========================================================================
|
||||
|
||||
procedure TMainForm.OnAudioReady(const Left, Right: array of Single;
|
||||
Count: Integer);
|
||||
var
|
||||
MonoBuf: array[0..1023] of Single;
|
||||
i: Integer;
|
||||
begin
|
||||
// Дублируем RX-аудио во встроенный динамик трансивера (DDC Audio, port 1028).
|
||||
if FController.FSendAudioToRadio then
|
||||
FController.FNetwork.SendSpeakerAudio(Left, Right, Count);
|
||||
if Assigned(FWebServer) and FWebServer.WebClientActive then
|
||||
begin
|
||||
for i := 0 to Count - 1 do
|
||||
MonoBuf[i] := (Left[i] + Right[i]) * 0.5;
|
||||
FWebServer.PushAudio(@MonoBuf[0], Count);
|
||||
Exit;
|
||||
end;
|
||||
FController.FAudioOut.Write(Left, Right, Count);
|
||||
end;
|
||||
|
||||
procedure TMainForm.OnSpectrumReady(const Pixels: array of Single;
|
||||
Count: Integer);
|
||||
var
|
||||
|
||||
@@ -79,6 +79,12 @@ type
|
||||
// Маршалинг произвольного метода в поток контроллера (GUI: TThread.Synchronize).
|
||||
TUIInvokeEvent = procedure(M: TThreadMethod) of object;
|
||||
|
||||
// Перехват RX-аудио потребителем (web-адаптер): вернуть True, если аудио
|
||||
// отправлено клиенту и НЕ должно идти в локальный AudioOut. Вызывается из
|
||||
// DSP-потока (как сам OnAudioReady) — реализация должна быть thread-safe.
|
||||
TAudioConsumeEvent = function(const Left, Right: array of Single;
|
||||
Count: Integer): Boolean of object;
|
||||
|
||||
// Снимок состояния для синхронизации нового фронтенда (Arduino/Web на connect).
|
||||
TRadioSnapshot = record
|
||||
VfoA, VfoB, CenterFreq, SpanHz: Double;
|
||||
@@ -110,6 +116,7 @@ type
|
||||
FOnInvoke: TUIInvokeEvent;
|
||||
FOnStateChanged: TRadioStateEvent;
|
||||
FOnAfterTune: TThreadMethod; // пост-тюн хук: канал-оркестрация после SetVfoA
|
||||
FOnAudioConsume: TAudioConsumeEvent; // web-перехват RX-аудио (если активен клиент)
|
||||
procedure Changed(Field: TRadioField);
|
||||
procedure ApplyAGCToEngine; // применяет FAGCMode к WDSP (единый маппинг)
|
||||
procedure ApplyModeDefaults; // дефолтный фильтр/девиация под текущий FMode
|
||||
@@ -265,6 +272,7 @@ type
|
||||
// ---- Колбэки движков (вызываются из рабочих потоков сети/DSP) ----
|
||||
// Чистая обработка данных без UI; навешиваются на FNetwork/FDSPEngine
|
||||
// вызывающим (GUI или демон) после CreateEngines.
|
||||
procedure OnAudioReady(const Left, Right: array of Single; Count: Integer);
|
||||
procedure OnMicPacket(const Data: TMicDataPacket);
|
||||
function PullSoundCardMic(MaxN: Integer): Integer;
|
||||
procedure OnDDCIQ(DDCIndex: Integer; const Data: TDDCIQPacket);
|
||||
@@ -429,6 +437,7 @@ type
|
||||
// рендера) — сюда вешается канальная оркестрация (ChannelController.OnVfoTuned),
|
||||
// чтобы любой путь тюнинга (desktop/web/CAT/энкодер) был канал-aware headless.
|
||||
property OnAfterTune: TThreadMethod read FOnAfterTune write FOnAfterTune;
|
||||
property OnAudioConsume: TAudioConsumeEvent read FOnAudioConsume write FOnAudioConsume;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -732,6 +741,19 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TRadioController.OnAudioReady(const Left, Right: array of Single;
|
||||
Count: Integer);
|
||||
// DSP-поток: маршрут RX-аудио. Встроенный динамик трансивера (DDC Audio,
|
||||
// port 1028) — независимый канал. Затем web-адаптер может перехватить аудио
|
||||
// (активный клиент); если перехвачено — в локальный AudioOut не пишем.
|
||||
begin
|
||||
if FSendAudioToRadio then
|
||||
FNetwork.SendSpeakerAudio(Left, Right, Count);
|
||||
if Assigned(FOnAudioConsume) and FOnAudioConsume(Left, Right, Count) then
|
||||
Exit;
|
||||
FAudioOut.Write(Left, Right, Count);
|
||||
end;
|
||||
|
||||
procedure TRadioController.OnMicPacket(const Data: TMicDataPacket);
|
||||
// HW mic-стрим уходит в WDSP только если выбран источник Radio; иначе сэмплы
|
||||
// игнорируются (mic берётся со звуковой карты).
|
||||
|
||||
@@ -102,6 +102,12 @@ type
|
||||
// подача в движок thread-safe (как OnMicPacket/OnDDCIQ контроллера).
|
||||
procedure OnMic(Samples: PSingle; Count: Integer);
|
||||
|
||||
// RX-аудио-перехват: навешивается на FController.OnAudioConsume. Если есть
|
||||
// активный web-клиент — сводим в моно и шлём ему, возвращаем True (локальный
|
||||
// AudioOut пропускается контроллером). Вызывается из DSP-потока.
|
||||
function AudioConsume(const Left, Right: array of Single;
|
||||
Count: Integer): Boolean;
|
||||
|
||||
// Исходящее зеркало: периодический снимок состояния + спектр/водопад в web.
|
||||
// Зовётся хозяином из GUI-таймера (буферы пикселей — у хозяина, остальное из
|
||||
// FController). Сервер сам гейтит по FRunning и шлёт активным клиентам.
|
||||
@@ -360,6 +366,22 @@ begin
|
||||
FController.FDSPEngine.PushTXMicSamplesD(Buf, n);
|
||||
end;
|
||||
|
||||
function TWebAdapter.AudioConsume(const Left, Right: array of Single;
|
||||
Count: Integer): Boolean;
|
||||
var
|
||||
MonoBuf: array[0..1023] of Single;
|
||||
i, n: Integer;
|
||||
begin
|
||||
Result := False;
|
||||
if not (Assigned(FServer) and FServer.WebClientActive) then Exit;
|
||||
n := Count;
|
||||
if n > Length(MonoBuf) then n := Length(MonoBuf);
|
||||
for i := 0 to n - 1 do
|
||||
MonoBuf[i] := (Left[i] + Right[i]) * 0.5;
|
||||
FServer.PushAudio(@MonoBuf[0], n);
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
// ── Исходящее зеркало состояния ──────────────────────────────────────────────
|
||||
|
||||
procedure TWebAdapter.PushState(const ASpec, AWf: array of Single);
|
||||
|
||||
Reference in New Issue
Block a user