diff --git a/WDSPEngine.pas b/WDSPEngine.pas index ac6d7b2..9edfe60 100644 --- a/WDSPEngine.pas +++ b/WDSPEngine.pas @@ -527,52 +527,56 @@ begin end; procedure TTXDSPThread.Execute; -// Тикает по таймеру с периодом одного 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 радио не голодал. +// Аналог tx_thread в piHPSDR/transmitter.c: +// Тикает с периодом одного mic-буфера (FAudioBufSize/FAudioRate). +// Если mic-данные накопились — обрабатывает их; если нет — обрабатывает тишину. +// Это обеспечивает непрерывный поток DUC IQ к железу и TX-сигнал на спектре/водопаде. var - Avail: Integer; - PeriodMs: Integer; - ProcessedAny: Boolean; + Avail: Integer; + PeriodMs: Integer; begin + // 512 сэмплов / 48000 Гц = ~10.67 мс → округляем вниз до 10 мс PeriodMs := Max(1, FEngine.FAudioBufSize * 1000 div FEngine.FAudioRate); while not Terminated do begin - // Ждём ровно PeriodMs. Сем сигналится только при Terminate → быстрый shutdown. + // Ждём сигнала от mic-данных ИЛИ истечения периода — + // в piHPSDR timing идёт от receive thread; здесь — от таймаута 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); - ProcessedAny := False; + // Обрабатываем накопившиеся блоки с реальными mic-данными repeat Avail := (FEngine.FTXMicHead - FEngine.FTXMicTail + TX_MIC_RING) and (TX_MIC_RING - 1); if (Avail >= FEngine.FAudioBufSize) and not Terminated then - begin - FEngine.ProcessTXBlock; - ProcessedAny := True; - end else + FEngine.ProcessTXBlock + else Break; until False; if Terminated then Break; - if not ProcessedAny then - FEngine.ProcessTXBlock; + // Если 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; end; end; @@ -1610,10 +1614,8 @@ begin end; // если буфер полон — сэмпл отбрасывается (overrun) end; - // Семафор намеренно не сигналим — TX-поток тикает по таймеру - // (PeriodMs = AudioBufSize/AudioRate). Сигнал давал бы немедленный - // возврат из WaitFor → лишний ProcessTXBlock → over-production - // IQ-потока относительно темпа радио → щелчки/FIFO-overrun. + + RTLEventSetEvent(FTXMicSem); // будим TX поток end; procedure TWDSPEngine.PushTXMicSampleD(const S: Double); @@ -1625,7 +1627,8 @@ begin FTXMicRing[FTXMicHead] := S; FTXMicHead := newHead; end; - // overrun → дроп, как в HW path. Сем не сигналим — см. PushTXMicSamples16. + // overrun → дроп, как в HW path + RTLEventSetEvent(FTXMicSem); end; procedure TWDSPEngine.PushTXMicSamplesD(const Src: array of Double; N: Integer); @@ -1646,7 +1649,9 @@ begin end; // overrun → дроп end; - // Сем не сигналим — см. PushTXMicSamples16. + + if cnt > 0 then + RTLEventSetEvent(FTXMicSem); end; // ---------------------------------------------------------------------------