mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
perf(dsp): молчащие слайсы считались от одного факта, что поднят TCI-сервер
RX_AUDIO по TCI снимается ДО громкости и мьюта, поэтому замьюченный слайс обязан продолжать считаться, пока его кто-то качает по сети — иначе клиент остаётся без звука ровно потому, что оператор убрал его у себя в комнате. Гейт в ProcessSlicesFor это и делал, но спрашивал ГЛОБАЛЬНЫЙ флаг «тапы аудио установлены», а его взводит SetTaps(True) на старте сервера. То есть галка «Enable TCI server» одна, без единого клиента, заставляла гонять полный fexchange0 по каждому молчащему слайсу — до шести штук, ~47 блоков в секунду на каждый, плюс два входа в критические секции на блок в DSP-потоке. Клиент, качающий один слайс из шести или вообще только IQ, оплачивал остальные так же. Флаг стал ПОСЛАЙСОВЫМ (TSliceChan.TapWanted, SetSliceTapWanted). Кто его выставляет — владелец потоков: TTCIAdapter.PushTapWants полностью пересчитывает картину по FStreams и раскладывает по слотам. Считаются только потоки RX_AUDIO: LINE_OUT и рекордер снимаются ПОСЛЕ мьюта, и на молчащем слайсе им достаётся тишина независимо от того, посчитали его или нет. Пересчёт зовётся отовсюду, где множество потоков или карта слайсов меняются: StartStream (синхронно и ДО ответа клиенту — тогда не теряется ни один блок), StopStream, DropClientStreams, DropDeadRxStreams, StopAllStreams и OnState по MapChanged — слот мог сменить хозяина, и разрешение обязано переехать вместе с ним. Список слушателей снимается под FStreamLock, и лок отпускается ДО похода в движок: DSP-поток входит в тап, уже держа FSliceLock движка, и берёт в нём FStreamLock — обратный порядок дал бы клин. Глобальные FAudioTapsActive/SetAudioTapsActive/PushAudioTapsActive удалены. FAudioTapCount остаётся: он по-прежнему гейтит сам вызов тапов (FireAudioTap) и сборку звука DMR. Из AttachEngineCallbacks флаг не восстанавливается намеренно — слайсы пересозданный движок не наследует вовсе. Заодно: StopStream завершал перебор через Exit прямо из-под try, минуя хвост процедуры, — заменено на Break. Стенд (+3 проверки, негативный контроль в обе стороны): мьют слайса не глушит RX_AUDIO по TCI; без потока молчащий слайс не считается вовсе (наблюдатель — обычный аудио-тап, который теперь права требовать демодуляции не имеет); попросили поток снова — слайс снова в работе. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+74
-1
@@ -1466,16 +1466,29 @@ type
|
||||
THost = class
|
||||
procedure DoInvoke(M: TThreadMethod);
|
||||
procedure OnTXIQ(const Buf: array of Double; Count: Integer);
|
||||
procedure OnTap(Kind: TRadioAudioKind; PanId, SliceId: Integer;
|
||||
const Left, Right: array of Single; Count: Integer);
|
||||
end;
|
||||
|
||||
var
|
||||
TXIQBlocks: Integer = 0;
|
||||
TXIQBlocks: Integer = 0;
|
||||
DemodBlocks: Integer = 0; // блоки rakDemod со слайса WatchSlice
|
||||
WatchSlice: Integer = 0;
|
||||
|
||||
procedure THost.DoInvoke(M: TThreadMethod);
|
||||
begin
|
||||
M();
|
||||
end;
|
||||
|
||||
procedure THost.OnTap(Kind: TRadioAudioKind; PanId, SliceId: Integer;
|
||||
const Left, Right: array of Single; Count: Integer);
|
||||
// Наблюдатель за выходом демодулятора слайса. Сам по себе считать слайс не
|
||||
// заставляет: тап — это слушатель, а не заказчик (см. TapWanted).
|
||||
begin
|
||||
if (Kind = rakDemod) and (SliceId = WatchSlice) and (Count > 0) then
|
||||
Inc(DemodBlocks);
|
||||
end;
|
||||
|
||||
procedure THost.OnTXIQ(const Buf: array of Double; Count: Integer);
|
||||
// Готовый блок TX-IQ: единственный наблюдаемый признак того, что аудио
|
||||
// клиента прошло весь тракт (ринг микрофона вычерпывается быстрее, чем его
|
||||
@@ -2086,6 +2099,66 @@ begin
|
||||
end;
|
||||
Check('аудио слайса идёт под номером 1', n > 0, IntToStr(n));
|
||||
|
||||
// ── ★Мьют слайса и стоимость молчащих слайсов ────────────────────────
|
||||
// RX_AUDIO снимается ДО громкости и мьюта, поэтому слайс, у которого
|
||||
// оператор убрал звук в комнате, обязан продолжать кормить клиента. Но
|
||||
// считается он ровно пока его кто-то качает: разрешение послайсовое
|
||||
// (TapWanted), а не «поднят TCI-сервер» — иначе шесть молчащих слайсов
|
||||
// жгли бы WDSP и без единого клиента.
|
||||
WatchSlice := SliceId;
|
||||
Ctrl.AddAudioTap(Host.OnTap);
|
||||
Ctrl.SetSliceMute(SliceId, True);
|
||||
C.Pump(100);
|
||||
while C.NextFrame(Op, Pay) do ; // выгребаем хвост
|
||||
DemodBlocks := 0;
|
||||
n := 0;
|
||||
for k := 0 to 40 do
|
||||
begin
|
||||
Ctrl.FDSPEngine.PushDDCPacket(Pkt, 0, PAIRS);
|
||||
Sleep(2);
|
||||
end;
|
||||
C.Pump(400);
|
||||
while C.NextFrame(Op, Pay) do
|
||||
if (Op = $02) and (Length(Pay) >= SizeOf(H)) then
|
||||
begin
|
||||
Move(Pay[0], H, SizeOf(H));
|
||||
if (H.StreamType = LongWord(Ord(tstRXAudio))) and (H.Receiver = 1) then
|
||||
Inc(n);
|
||||
end;
|
||||
Check('мьют слайса не глушит RX_AUDIO по TCI', n > 0, IntToStr(n));
|
||||
|
||||
// Поток убрали — молчащий слайс не должен считаться вовсе. Тап при этом
|
||||
// остаётся навешенным: он наблюдатель и права требовать демодуляции не
|
||||
// имеет (раньше именно он её и включал — на все слайсы разом).
|
||||
C.SendText('audio_stop:1;');
|
||||
C.Pump(200);
|
||||
DemodBlocks := 0;
|
||||
for k := 0 to 40 do
|
||||
begin
|
||||
Ctrl.FDSPEngine.PushDDCPacket(Pkt, 0, PAIRS);
|
||||
Sleep(2);
|
||||
end;
|
||||
Sleep(100);
|
||||
Check('без потока молчащий слайс не считается', DemodBlocks = 0,
|
||||
IntToStr(DemodBlocks));
|
||||
|
||||
// И обратно: попросили поток — слайс снова в работе.
|
||||
C.SendText('audio_start:1;');
|
||||
C.Pump(200);
|
||||
DemodBlocks := 0;
|
||||
for k := 0 to 40 do
|
||||
begin
|
||||
Ctrl.FDSPEngine.PushDDCPacket(Pkt, 0, PAIRS);
|
||||
Sleep(2);
|
||||
end;
|
||||
Sleep(100);
|
||||
Check('поток вернулся — слайс снова считается', DemodBlocks > 0,
|
||||
IntToStr(DemodBlocks));
|
||||
Ctrl.RemoveAudioTap(Host.OnTap);
|
||||
Ctrl.SetSliceMute(SliceId, False);
|
||||
C.Pump(200);
|
||||
while C.NextFrame(Op, Pay) do ;
|
||||
|
||||
// ── ★TX со слайса: маркер обязан нести НОМЕР ЭТОГО приёмника ─────────
|
||||
// MSHV шлёт TX-аудио только в ответ на маркер TX_CHRONO и отбрасывает
|
||||
// ЛЮБОЙ входящий блок с чужим receiver (network.cpp:231 — `if
|
||||
|
||||
Reference in New Issue
Block a user