diff --git a/WDSPEngine.pas b/WDSPEngine.pas index 9edfe60..ac6d7b2 100644 --- a/WDSPEngine.pas +++ b/WDSPEngine.pas @@ -527,56 +527,52 @@ begin end; procedure TTXDSPThread.Execute; -// Аналог tx_thread в piHPSDR/transmitter.c: -// Тикает с периодом одного mic-буфера (FAudioBufSize/FAudioRate). -// Если mic-данные накопились — обрабатывает их; если нет — обрабатывает тишину. -// Это обеспечивает непрерывный поток DUC IQ к железу и TX-сигнал на спектре/водопаде. +// Тикает по таймеру с периодом одного mic-буфера (FAudioBufSize/FAudioRate). +// Семафор НЕ используется как сигнал «есть mic-данные» — он бы давал +// немедленные возвраты из WaitFor (auto-reset event) и приводил к +// over-production IQ-потока: за один реальный период радио мы шлём +// 2–3 блока вместо одного. Это и было слышно в TUN как щелчки. +// Семафор оставлен только для будилки на shutdown (Terminate → Set). +// +// За один тик: +// - Pull mic из PortAudio (sound-card path). +// - Inner loop: процессим все накопившиеся полные блоки (обычно 1). +// - Если ничего реального не процессили — гоним один блок тишины, +// чтобы DUC IQ поток не прерывался и FIFO радио не голодал. var - Avail: Integer; - PeriodMs: Integer; + Avail: Integer; + PeriodMs: Integer; + ProcessedAny: Boolean; begin - // 512 сэмплов / 48000 Гц = ~10.67 мс → округляем вниз до 10 мс PeriodMs := Max(1, FEngine.FAudioBufSize * 1000 div FEngine.FAudioRate); while not Terminated do begin - // Ждём сигнала от mic-данных ИЛИ истечения периода — - // в piHPSDR timing идёт от receive thread; здесь — от таймаута + // Ждём ровно PeriodMs. Сем сигналится только при Terminate → быстрый shutdown. RTLEventWaitFor(FEngine.FTXMicSem, PeriodMs); if Terminated then Break; if not FEngine.FTXActive then Continue; - // Если выбран sound-card mic — тянем сэмплы из внешнего источника - // (PortAudio через FOnPullMic в MainForm) ПЕРЕД проверкой ring buffer. - // Callback сам пушит сэмплы в FTXMicRing через PushTXMicSampleD. if (FEngine.FTXMicSource = txmsSoundCard) and Assigned(FEngine.FOnPullMic) then FEngine.FOnPullMic(FEngine.FAudioBufSize * 2); - // Обрабатываем накопившиеся блоки с реальными mic-данными + ProcessedAny := 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 - else + begin + FEngine.ProcessTXBlock; + ProcessedAny := True; + end else Break; until False; if Terminated then Break; - // Если mic-данных не было — обрабатываем один блок тишины, чтобы - // DUC IQ поток не прерывался и спектр/водопад обновлялись. - // НО для sound-card mic неполный блок (0 < Avail < AudioBufSize) нельзя - // подменять тишиной: PortAudio часто отдаёт 128-сэмпловые callback'и, - // и если в кольце уже есть голосовой хвост — он застрянет, а в эфир - // уйдёт тишина. Слышно как чередование «голос/тишина» = треск ≈50–100 Гц. - // В этом случае ждём следующий тик, хвост накопится до полного блока. - 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 ProcessedAny then + FEngine.ProcessTXBlock; end; end; @@ -1614,8 +1610,10 @@ begin end; // если буфер полон — сэмпл отбрасывается (overrun) end; - - RTLEventSetEvent(FTXMicSem); // будим TX поток + // Семафор намеренно не сигналим — TX-поток тикает по таймеру + // (PeriodMs = AudioBufSize/AudioRate). Сигнал давал бы немедленный + // возврат из WaitFor → лишний ProcessTXBlock → over-production + // IQ-потока относительно темпа радио → щелчки/FIFO-overrun. end; procedure TWDSPEngine.PushTXMicSampleD(const S: Double); @@ -1627,8 +1625,7 @@ begin FTXMicRing[FTXMicHead] := S; FTXMicHead := newHead; end; - // overrun → дроп, как в HW path - RTLEventSetEvent(FTXMicSem); + // overrun → дроп, как в HW path. Сем не сигналим — см. PushTXMicSamples16. end; procedure TWDSPEngine.PushTXMicSamplesD(const Src: array of Double; N: Integer); @@ -1649,9 +1646,7 @@ begin end; // overrun → дроп end; - - if cnt > 0 then - RTLEventSetEvent(FTXMicSem); + // Сем не сигналим — см. PushTXMicSamples16. end; // ---------------------------------------------------------------------------