mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
fix(tci,cat): резерв бюджета переезжает писателю; строгий разбор полей ZZ-команд
Три замечания по79132f1. 1. [P2] Лимит памяти рекордеров обходился при SAVE. Take собирал сплошную копию записи, а DropData тут же возвращал исходные куски в бюджет — копия жила дальше в асинхронном TTCIWavWriter уже неучтённой. Один SAVE поднимал настоящее потребление примерно вдвое, а на медленном или зависшем сетевом каталоге очередь writer-потоков и их буферов росла мимо потолка вовсе. Теперь копия НАСЛЕДУЕТ резерв кусков, из которых собрана: Take отдаёт его out-параметром Reserved, писатель держит до конца записи и отпускает в ReleaseData — ровно один раз, сколько бы путей выхода ни было у Execute. Новых денег у бюджета копия не берёт, так что потолок теперь считает и очередь сохранений тоже. 2. [P2] Пять ZZ-команд проверяли длину поля, но не содержимое: StrToIntDef(s, 0) превращал любую нечисловую пару символов в индекс 0. ZZBSxx; переключал диапазон на нулевой вместо ?;, ZZBMxx; и ZZAUxx;/ZZBPxx; двигали VFO, ZZFIxx; выбирал фильтр 0. Разбор приведён к идиоме ZZFL/ZZFH: TryStrToInt, иначе ошибка формата. 3. [P3] Сообщение об отказе запуска TCI звало в Settings → Advanced, а настройки там уже не живут — вкладка CAT (переезд был вde0830f). Стенд 194/194: Take отдаёт резерв размером с копию, после Take занят ровно он, писатель возвращает его по окончании. Без фикса первая проверка краснеет. GUI (--ws=qt6) и демон зелёные. doc/TCI.md §2.5 и сводка стенда, doc/CAT_STATUS.md — таблица разбора. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+40
-10
@@ -562,12 +562,13 @@ var
|
||||
Sz: LongWord;
|
||||
W: TTCIWavWriter;
|
||||
Waited: Integer;
|
||||
Was: Int64;
|
||||
Was, Held: Int64;
|
||||
begin
|
||||
WriteLn('C. Рекордер линейного выхода');
|
||||
|
||||
// Максимум записи — 1 секунда: подаём полторы, лишнее не берём. ★Именно НЕ
|
||||
// берём: окно записи по §4.3 начинается со START, а не «последняя секунда».
|
||||
Was := TCIRecBudgetUsed; // счёт бюджета ДО записи
|
||||
Rec := TTCIRecorder.Create(0, 48000, 1, nil);
|
||||
try
|
||||
Total := 0;
|
||||
@@ -577,12 +578,25 @@ begin
|
||||
Rec.Feed(L, R, 8192);
|
||||
Inc(Total, 8192);
|
||||
end;
|
||||
Data := Rec.Take;
|
||||
Data := Rec.Take(Held);
|
||||
Check('рекордер: длина по максимуму', Length(Data) = 48000 * 2,
|
||||
IntToStr(Length(Data)));
|
||||
// ★Take отдаёт данные ВМЕСТЕ с их местом в бюджете: копия живёт дальше в
|
||||
// писателе, и до конца записи она обязана оставаться учтённой. Иначе SAVE
|
||||
// был бы дырой в потолке — куски вернулись бы сразу, а копия висела бы
|
||||
// неучтённой, и очередь сохранений на медленном диске росла бы без границ.
|
||||
Check('рекордер: Take передаёт резерв размером с копию',
|
||||
Held = Int64(Length(Data)) * SizeOf(SmallInt),
|
||||
IntToStr(Held));
|
||||
Check('рекордер: после Take занят ровно резерв копии',
|
||||
TCIRecBudgetUsed = Was + Held,
|
||||
IntToStr(TCIRecBudgetUsed - Was));
|
||||
TCIRecBudgetFree(Held); // как это сделает писатель
|
||||
Check('рекордер: возврат резерва закрывает счёт',
|
||||
TCIRecBudgetUsed = Was, IntToStr(TCIRecBudgetUsed - Was));
|
||||
Check('рекордер: уровень сохранён',
|
||||
(Data[0] = Round(0.25 * 32767)) and (Data[1] = -Round(0.25 * 32767)));
|
||||
Check('рекордер: Take завершает запись', Length(Rec.Take) = 0);
|
||||
Check('рекордер: Take завершает запись', Length(Rec.Take(Held)) = 0);
|
||||
finally
|
||||
Rec.Free;
|
||||
end;
|
||||
@@ -596,7 +610,7 @@ begin
|
||||
R[0] := 0;
|
||||
Rec.Feed(L, R, 1);
|
||||
end;
|
||||
Data := Rec.Take;
|
||||
Data := Rec.Take(Held);
|
||||
Check('рекордер: с начала записи, а не с конца',
|
||||
(Length(Data) = 2000) and (Data[0] = 0) and
|
||||
(Abs(Data[1998] - Round((999 / 4000.0) * 32767)) <= 1),
|
||||
@@ -611,7 +625,7 @@ begin
|
||||
try
|
||||
for i := 0 to 8191 do begin L[i] := 0.25; R[i] := -0.25; end;
|
||||
Rec.Feed(L, R, 8192);
|
||||
Check('рекордер: до срока запись есть', Length(Rec.Take) > 0);
|
||||
Check('рекордер: до срока запись есть', Length(Rec.Take(Held)) > 0);
|
||||
finally
|
||||
Rec.Free;
|
||||
end;
|
||||
@@ -619,9 +633,9 @@ begin
|
||||
try
|
||||
Rec.Feed(L, R, 8192);
|
||||
Sleep(1100); // окно закрылось
|
||||
Check('рекордер: после срока запись удалена', Length(Rec.Take) = 0);
|
||||
Check('рекордер: после срока запись удалена', Length(Rec.Take(Held)) = 0);
|
||||
Rec.Feed(L, R, 8192); // и новое аудио уже не принимает
|
||||
Check('рекордер: истёкший не оживает', Length(Rec.Take) = 0);
|
||||
Check('рекордер: истёкший не оживает', Length(Rec.Take(Held)) = 0);
|
||||
finally
|
||||
Rec.Free;
|
||||
end;
|
||||
@@ -654,7 +668,7 @@ begin
|
||||
try
|
||||
Rec.Feed(L, R, 8192);
|
||||
Check('бюджет: при отказе запись не рушится, а стоит пустой',
|
||||
Length(Rec.Take) = 0);
|
||||
Length(Rec.Take(Held)) = 0);
|
||||
finally
|
||||
Rec.Free;
|
||||
end;
|
||||
@@ -683,7 +697,7 @@ begin
|
||||
DeleteFile(Path);
|
||||
SetLength(Data, 2000);
|
||||
for i := 0 to 1999 do Data[i] := i * 8;
|
||||
W := TTCIWavWriter.Create(Path, Data, 48000);
|
||||
W := TTCIWavWriter.Create(Path, Data, 48000, 0);
|
||||
Waited := 0;
|
||||
while (not FileExists(Path)) and (Waited < 2000) do
|
||||
begin
|
||||
@@ -713,12 +727,28 @@ begin
|
||||
finally
|
||||
FS.Free;
|
||||
end;
|
||||
// ★Писатель отпускает резерв, когда данные ему больше не нужны — иначе
|
||||
// потолок памяти держал бы только сами записи, а очередь сохранений на
|
||||
// медленном диске росла бы мимо него.
|
||||
Was := TCIRecBudgetUsed;
|
||||
Check('WAV: резерв под писателя взят', TCIRecBudgetTake(4096));
|
||||
DeleteFile(Path);
|
||||
TTCIWavWriter.Create(Path, Data, 48000, 4096);
|
||||
Waited := 0;
|
||||
while (TCIRecBudgetUsed <> Was) and (Waited < 2000) do
|
||||
begin
|
||||
Sleep(10);
|
||||
Inc(Waited, 10);
|
||||
end;
|
||||
Check('WAV: писатель вернул резерв по окончании',
|
||||
TCIRecBudgetUsed = Was, IntToStr(TCIRecBudgetUsed - Was));
|
||||
|
||||
// ★Существующий файл не трогаем: имя приходит из сети, и fmCreate затирал
|
||||
// бы любой доступный процессу файл. Пишем поверх заведомо другой длиной —
|
||||
// файл обязан остаться прежним.
|
||||
SetLength(Data, 10);
|
||||
for i := 0 to 9 do Data[i] := 1;
|
||||
TTCIWavWriter.Create(Path, Data, 48000);
|
||||
TTCIWavWriter.Create(Path, Data, 48000, 0);
|
||||
Sleep(200);
|
||||
FS := TFileStream.Create(Path, fmOpenRead);
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user