mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
fix(tci): SAVE отдаёт писателю сами куски записи, а не сплошную копию
Потолок 128 МБ всё ещё пробивался примерно вдвое на пике: Take собирал
линейную копию ДО освобождения FChunks, поэтому при полном бюджете рядом
жили ~128 МБ кусков и ~128 МБ копии, а счётчик показывал 128. Передача
резерва писателю (9b77809) закрывала учёт, но не сам пик.
Копии больше нет вовсе. Take отдаёт куски КАК ЕСТЬ — новый TTCIRecTake
(Chunks + Chunk + Count + Reserved), — и вместе с ними уезжает их место в
бюджете целиком. TTCIWavWriter пишет куски в файл подряд: в WAV сэмплы и
так лежат встык, а хвост последнего куска за Count просто не наш. Место
отпускается в ReleaseData, ровно один раз на любом пути выхода Execute.
Продовый путь теперь не выделяет под запись ни одного лишнего байта:
TTCIPcm остался только внутри куска. Склейка нужна одному стенду, чтобы
проверять порядок и уровень, — она и живёт в стенде (FlatTake).
Стенд 199/199: 2.5 с записи отдаются ТРЕМЯ кусками по секунде (свёрнутая
копия дала бы один), счёт бюджета при Take не меняется, резерва хватает
на отданные куски, склеенные куски дают непрерывный звук, писатель
возвращает резерв по окончании. Проверено, что копирующая реализация Take
краснит четыре проверки. GUI (--ws=qt6) и демон зелёные.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+15
-9
@@ -2998,8 +2998,7 @@ procedure TTCIAdapter.CmdRecorder(Client: TTCIClient; const M: TTCIMessage);
|
||||
var
|
||||
Rx, Sec, Rate: Integer;
|
||||
Path, Req: string;
|
||||
Data: TTCIPcm;
|
||||
Held: Int64; // байты бюджета, уходящие писателю вместе с Data
|
||||
Rec: TTCIRecTake; // куски записи + их место в бюджете
|
||||
Old, New_: TTCIRecorder;
|
||||
begin
|
||||
if not TCITryArgInt(M, 0, Rx) or not ValidRx(Rx) then
|
||||
@@ -3085,8 +3084,10 @@ begin
|
||||
// Забираем рекордер из таблицы (сохранение завершает запись, §4.3) и только
|
||||
// потом снимаем с него данные: копия кольца — это десятки мегабайт, и делать
|
||||
// её под локом DSP-потока нельзя.
|
||||
Data := nil;
|
||||
Held := 0;
|
||||
Rec.Chunks := nil;
|
||||
Rec.Chunk := 0;
|
||||
Rec.Count := 0;
|
||||
Rec.Reserved := 0;
|
||||
Rate := TCI_AUDIO_ENGINE_RATE;
|
||||
FStreamLock.Enter;
|
||||
try
|
||||
@@ -3097,20 +3098,25 @@ begin
|
||||
end;
|
||||
if Old <> nil then
|
||||
begin
|
||||
// ★Резерв бюджета переезжает вместе с данными: копию держит писатель, и
|
||||
// отпустит её он же. Иначе SAVE был бы дырой в потолке памяти.
|
||||
Data := Old.Take(Held);
|
||||
// ★Куски записи переезжают писателю КАК ЕСТЬ, вместе со своим местом в
|
||||
// бюджете: сплошная копия удваивала бы пик, а невыпущенный резерв был бы
|
||||
// дырой в потолке.
|
||||
Rec := Old.Take;
|
||||
Rate := Old.Rate;
|
||||
Old.Free;
|
||||
end;
|
||||
if Data = nil then
|
||||
if Rec.Count <= 0 then
|
||||
begin
|
||||
// Резерва тут быть неоткуда (кусок выделяется только под сэмпл, который
|
||||
// тут же и пишется), но возвращаем на всякий случай: единственный путь,
|
||||
// на котором запись не доходит до писателя.
|
||||
TCIRecBudgetFree(Rec.Reserved);
|
||||
Reply(Client, TCIBuild('tci_error', [LowerCase(M.Name), 'nothing recorded']));
|
||||
Exit;
|
||||
end;
|
||||
// Пишет отдельный поток: файл может быть в десятки мегабайт, а мы сейчас в
|
||||
// потоке клиента, который в это время не читает свой сокет.
|
||||
TTCIWavWriter.Create(Path, Data, Rate, Held);
|
||||
TTCIWavWriter.Create(Path, Rec, Rate);
|
||||
end;
|
||||
|
||||
procedure TTCIAdapter.DropClientRecorders(C: TTCIClient);
|
||||
|
||||
Reference in New Issue
Block a user