diff --git a/AudioOutput.pas b/AudioOutput.pas
index b6b9df2..da264de 100644
--- a/AudioOutput.pas
+++ b/AudioOutput.pas
@@ -189,6 +189,7 @@ const
);
{$ENDIF}
{$ENDIF}
+const
PA_NO_ERROR = 0;
PA_FLOAT32 = LongWord(1);
PA_NO_FLAG = LongWord(0);
diff --git a/MainForm.pas b/MainForm.pas
index cf29837..513e05f 100644
--- a/MainForm.pas
+++ b/MainForm.pas
@@ -1155,18 +1155,16 @@ begin
rfVfoA:
begin
FreqDispA.Frequency := Round(FController.FVfoA);
- // Не рисуем синхронно из wheel/drag: частые события забивают UI-поток.
- // Invalidate лишь планирует; DrawSpectrum откладываем (FSpectrumDirty).
- // Линейку дёргаем сразу — её рендер дешёвый, иначе отстаёт от спектра.
+ // Не рисуем синхронно из wheel/drag: частые события забивают UI-поток,
+ // а мгновенный Invalidate давал GL-кадр на КАЖДОЕ событие мыши (при
+ // драге рендер упирался в vsync и обходил FPS-гейт — на AMD APU это
+ // трещало в HDMI-звуке того же GPU). Кадр отрисует ближайший тик
+ // FSpectrumTimer по FSpectrumDirty. Линейка дешёвая — её сразу.
SyncSpecViewFreq;
FSpectrumDirty := True;
- PbSpectrum.Invalidate;
if PbRuler <> nil then PbRuler.Invalidate;
if FController.FLastVfoScrolled then
- begin
FWaterfallDirty := True;
- if PbWaterfall <> nil then PbWaterfall.Invalidate;
- end;
end;
rfActiveVfo:
begin
@@ -1182,11 +1180,11 @@ begin
rfCenterFreq:
begin
// CTUN-скролл центра (drag/web): ruler-метки сдвинулись → инвалидируем
- // кэш; спектр откладываем (FSpectrumDirty) — drag шлёт много событий.
+ // кэш; спектр откладываем (FSpectrumDirty) — drag шлёт много событий,
+ // кадр отрисует таймер (FPS-гейт, см. rfVfoA).
FSpecView.InvalidateRulerCache;
SyncSpecViewFreq;
FSpectrumDirty := True;
- PbSpectrum.Invalidate;
if PbRuler <> nil then PbRuler.Invalidate;
end;
rfVfoB:
@@ -1196,8 +1194,7 @@ begin
begin
FSpecView.InvalidateRulerCache;
SyncSpecViewFreq;
- FSpectrumDirty := True;
- PbSpectrum.Invalidate;
+ FSpectrumDirty := True; // кадр — по таймеру (FPS-гейт, см. rfVfoA)
if PbRuler <> nil then PbRuler.Invalidate;
end;
end;
@@ -4568,8 +4565,7 @@ begin
FController.SetSliceTarget(FSliceDragId, NewHz);
PushSliceFlagState(FSliceDragId); // обновить частоту во флаге
PositionSliceFlags;
- FSpectrumDirty := True;
- PbSpectrum.Invalidate;
+ FSpectrumDirty := True; // кадр — по таймеру (FPS-гейт, как при drag VFO)
end;
Exit;
end;
diff --git a/WDSPEngine.pas b/WDSPEngine.pas
index 633027c..dbaa360 100644
--- a/WDSPEngine.pas
+++ b/WDSPEngine.pas
@@ -1259,6 +1259,17 @@ begin
FreeAndNil(FDSPThread);
end;
+ // TX-DSP поток живёт весь сеанс (не пересоздаётся на T/R) — гасим его здесь,
+ // до закрытия WDSP-каналов (он зовёт fexchange0 на TXA_CHAN).
+ FTXActive := False;
+ if Assigned(FTXThread) then
+ begin
+ FTXThread.Terminate;
+ RTLEventSetEvent(FTXMicSem);
+ FTXThread.WaitFor;
+ FreeAndNil(FTXThread);
+ end;
+
// Закрываем WDSP-каналы слайсов (записи FSlices сохраняем — переоткроются в Open).
FSliceLock.Enter;
try
@@ -2130,37 +2141,30 @@ end;
procedure TWDSPEngine.SetTXRun(Run: Boolean);
begin
- FTXActive := Run;
FDispPos := 0;
FTXDispPos := 0;
- if Run then
- FActiveDisplayID := TX_DISP_ID
- else
- FActiveDisplayID := RX_DISP_ID;
-
if Run then
begin
- if FInitialized then
- SetChannelState(TXA_CHAN, 1, 0); // run, no delay
- // Сброс mic ring buffer и запуск TX потока
+ FActiveDisplayID := TX_DISP_ID;
+ // Сброс mic ring ДО активации, чтобы простаивавший поток не прочитал старое.
FTXMicHead := 0;
FTXMicTail := 0;
+ if FInitialized then
+ SetChannelState(TXA_CHAN, 1, 0); // run, no delay
+ // TX-DSP поток создаётся ОДИН раз и живёт весь сеанс. НЕ пересоздаём на каждом
+ // T/R: join (WaitFor) давал фиксированные ~100 мс фриза GUI на отпускании PTT.
+ // При FTXActive=False поток простаивает (см. TTXDSPThread.Execute: Continue).
if not Assigned(FTXThread) then
begin
FTXThread := TTXDSPThread.Create(Self);
FTXThread.Priority := tpHighest;
end;
+ FTXActive := True; // активируем последним
end else begin
+ FTXActive := False; // поток уходит в простой (не трогает канал/ring)
+ FActiveDisplayID := RX_DISP_ID;
if FInitialized then
- SetChannelState(TXA_CHAN, 0, 1); // stop, with slew
- // Останавливаем TX поток
- if Assigned(FTXThread) then
- begin
- FTXThread.Terminate;
- RTLEventSetEvent(FTXMicSem);
- FTXThread.WaitFor;
- FreeAndNil(FTXThread);
- end;
+ SetChannelState(TXA_CHAN, 0, 1); // stop, with slew (поток уже idle)
FTXMicHead := 0;
FTXMicTail := 0;
end;
diff --git a/ewsdr.lpi b/ewsdr.lpi
index 4a338f8..3b6971b 100644
--- a/ewsdr.lpi
+++ b/ewsdr.lpi
@@ -17,9 +17,9 @@
-
+
-
+
@@ -83,6 +83,7 @@
+