From 4b715c8b036f660512c1748636c5cb18d31d2d3a Mon Sep 17 00:00:00 2001 From: Uladzimir Karpenka Date: Thu, 20 Aug 2026 16:05:29 +0300 Subject: [PATCH] =?UTF-8?q?fix(tci):=20=D1=81=D0=BC=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BA=20TCI=20=D0=BE?= =?UTF-8?q?=D1=81=D0=B2=D0=BE=D0=B1=D0=BE=D0=B6=D0=B4=D0=B0=D0=BB=D0=B0=20?= =?UTF-8?q?=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=D0=BE=D0=B2,=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D1=81=D0=BD=D1=8F=D0=B2=20=D1=82=D0=B0=D0=BF=D1=8B=20?= =?UTF-8?q?DSP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ApplySettings гасил сервер в порядке Stop → StopAllStreams → SetTaps(False), а TTCIServer.Stop на шаге 5 освобождает всех клиентов. Тапы аудио и IQ в этот момент ещё стояли, и DSP-поток, войдя в OnAudioTap между возвратом из Stop и захватом FStreamLock в StopAllStreams, шёл по FStreams в FeedAudio → EmitFull → FClient.SendBin, то есть брал FOutLock уже освобождённого объекта. Достаточно было сменить порт или снять галку «Enable» в Settings → CAT, пока у клиента жив AUDIO_START или IQ_START. Порядок приведён к тому, что и так был в Destroy: ПЕРВЫМИ снимаем тапы (их снятие ждёт выхода DSP-потока из вызова), потом останавливаем сервер, потом гасим потоки — их объекты ссылаются на клиентов, поэтому они последние. Co-Authored-By: Claude Opus 5 --- TCIAdapter.pas | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/TCIAdapter.pas b/TCIAdapter.pas index a599747..c4fcd73 100644 --- a/TCIAdapter.pas +++ b/TCIAdapter.pas @@ -470,11 +470,16 @@ begin Exit(False); OldPort := FServer.Port; - FServer.Stop; - // Сервер остановлен — клиентов больше нет, значит и потоки чужие: снимаем - // тапы, иначе DSP-поток продолжал бы носить аудио в никуда. - StopAllStreams; + // ★ПЕРВЫМИ снимаем тапы — тот же порядок, что и в Destroy, и по той же + // причине: их зовёт DSP-поток. Раньше здесь сначала звали FServer.Stop, а он + // на шаге 5 освобождает клиентов; тапы в этот момент ещё стояли, и DSP-поток, + // войдя в OnAudioTap между Stop и StopAllStreams, писал в уже освобождённого + // клиента (FStreams[i].FeedAudio -> EmitFull -> FClient.SendBin). Ловилось + // сменой порта или снятием галки «Enable» при живом AUDIO_START/IQ_START. SetTaps(False); + FServer.Stop; + // Потоки — после остановки сервера: их объекты ссылаются на клиентов. + StopAllStreams; if not T.Enabled then begin FCfg := T;