Fix DUC IQ pacing for TX tune

This commit is contained in:
2026-05-07 22:42:13 +03:00
parent c59c6c8bcc
commit 6067aae3dc
2 changed files with 256 additions and 18 deletions
+29 -13
View File
@@ -534,12 +534,22 @@ procedure TTXDSPThread.Execute;
var
Avail: Integer;
PeriodMs: Integer;
PeriodUs: Integer;
AccUs: Integer;
DidBlock: Boolean;
begin
// 512 сэмплов / 48000 Гц = ~10.67 мс → округляем вниз до 10 мс
PeriodMs := Max(1, FEngine.FAudioBufSize * 1000 div FEngine.FAudioRate);
// 512 samples / 48000 Hz = 10.666 ms. Do not floor this to 10 ms:
// that over-produces TX IQ by ~6.7%, eventually forcing DUC queue
// corrections/drops and producing periodic sidebands on a pure TUN tone.
PeriodUs := Max(1000, FEngine.FAudioBufSize * 1000000 div FEngine.FAudioRate);
AccUs := 0;
while not Terminated do
begin
Inc(AccUs, PeriodUs);
PeriodMs := Max(1, AccUs div 1000);
Dec(AccUs, PeriodMs * 1000);
// Ждём сигнала от mic-данных ИЛИ истечения периода —
// в piHPSDR timing идёт от receive thread; здесь — от таймаута
RTLEventWaitFor(FEngine.FTXMicSem, PeriodMs);
@@ -554,29 +564,35 @@ begin
FEngine.FOnPullMic(FEngine.FAudioBufSize * 2);
// Обрабатываем накопившиеся блоки с реальными mic-данными
DidBlock := False;
repeat
Avail := (FEngine.FTXMicHead - FEngine.FTXMicTail + TX_MIC_RING)
and (TX_MIC_RING - 1);
if (Avail >= FEngine.FAudioBufSize) and not Terminated then
FEngine.ProcessTXBlock
begin
FEngine.ProcessTXBlock;
DidBlock := True;
end
else
Break;
until False;
if Terminated then Break;
// Если mic-данных не было — обрабатываем один блок тишины, чтобы
// DUC IQ поток не прерывался и спектр/водопад обновлялись.
// НО для sound-card mic неполный блок (0 < Avail < AudioBufSize) нельзя
// подменять тишиной: PortAudio часто отдаёт 128-сэмпловые callback'и,
// и если в кольце уже есть голосовой хвост — он застрянет, а в эфир
// уйдёт тишина. Слышно как чередование «голос/тишина» = треск ≈50–100 Гц.
// В этом случае ждём следующий тик, хвост накопится до полного блока.
// Если на этом тике не было ни одного полного mic-блока и mic-данных
// вообще нет — обрабатываем один блок тишины, чтобы DUC IQ поток не
// прерывался и спектр/водопад обновлялись.
// Важно: не добавлять silence-block сразу после реального блока. Для TUN
// этот "silence" тоже содержит PostGen tone, поэтому лишний блок ломает
// темп DUC IQ и даёт периодические боковые/тр-р-р.
// Если же в ring уже есть неполный блок, нельзя подменять его тишиной:
// radio mic приходит пакетами по 64 сэмпла, sound-card часто по 128.
// Иначе каждый mic packet будит поток раньше срока, а мы генерируем
// лишний TX-блок тишины вместо ожидания полного 512-sample блока.
Avail := (FEngine.FTXMicHead - FEngine.FTXMicTail + TX_MIC_RING)
and (TX_MIC_RING - 1);
if (Avail = 0) or (FEngine.FTXMicSource <> txmsSoundCard) then
if Avail < FEngine.FAudioBufSize then
FEngine.ProcessTXBlock;
if (not DidBlock) and (Avail = 0) then
FEngine.ProcessTXBlock;
end;
end;