feat(slices): аудио-устройства слайса на вкладке Slices (in/out на слот)

У каждого слайса B..G — свои Audio out / Audio in рядом с его CAT-портом.
Списки те же, что у главных комбо; пустой выбор (Default/None) = прежнее
поведение, общий выход/вход приложения.

Это устройство СЛОТА (буквы), а не шаблон:
  • смена настройки применяется к слайсу, стоящему на слоте, немедленно
    (SetSliceSlotAudio) — как и вся остальная форма настроек EWSDR;
  • слайс, который встанет на слот позже (создание/restore из персиста),
    получает то же устройство: слот сильнее аргумента AddSlice;
  • выбор во вкладке AUD флага пишется обратно в настройку слота
    (StoreSliceSlotAudio) — вкладка всегда показывает реальность, а не
    вторую, конкурирующую истину.

Хранятся ИМЕНА PortAudio-устройств, а не индексы: порядок перечисления
между запусками плавает. Имя устройства, которого сейчас нет в системе,
не теряется — комбо добавляет его в список и держит выбранным.

ApplySliceSlotAudio зовётся на старте и на смене устройства (rfDevice):
слайсы восстанавливаются позже, слот должен знать свои звуковухи заранее.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-25 14:33:42 +03:00
co-authored by Claude Opus 5
parent 2d15da0462
commit 20186ce132
5 changed files with 284 additions and 13 deletions
+15
View File
@@ -460,6 +460,11 @@ type
// Auto TX (аналог «Auto Switch TX Slice» в SmartSDR CAT): команда TX по
// CAT-порту слайса сама назначает этот слайс источником передачи.
CATSliceAutoTX: array[0..CFG_CAT_SLICE_COUNT-1] of Boolean;
// Аудио слота слайса: имена PortAudio-устройств ('' = общий выход
// приложения / общий вход). Имена, а не индексы: порядок перечисления
// устройств меняется от запуска к запуску.
CATSliceOutDev: array[0..CFG_CAT_SLICE_COUNT-1] of string;
CATSliceInDev: array[0..CFG_CAT_SLICE_COUNT-1] of string;
end;
TSettingsManager = class
@@ -665,6 +670,8 @@ begin
G.CATSliceEnabled[i] := False;
G.CATSlicePort[i] := CFG_CAT_SLICE_PORT0 + i;
G.CATSliceAutoTX[i] := False;
G.CATSliceOutDev[i] := '';
G.CATSliceInDev[i] := '';
end;
// ADC — включены по умолчанию: поддерживаются на Hermes, Angelia, Orion
G.DitherEnabled := True;
@@ -923,6 +930,8 @@ begin
G.CATSliceEnabled[i] := JB(GObj,'cat_slice_en_' +IntToStr(i), False);
G.CATSlicePort[i] := JI(GObj,'cat_slice_port_' +IntToStr(i), CFG_CAT_SLICE_PORT0 + i);
G.CATSliceAutoTX[i] := JB(GObj,'cat_slice_autotx_'+IntToStr(i), False);
G.CATSliceOutDev[i] := JS(GObj,'cat_slice_out_' +IntToStr(i), '');
G.CATSliceInDev[i] := JS(GObj,'cat_slice_in_' +IntToStr(i), '');
end;
G.CATTcpEnabled := JB(GObj,'cat_tcp_enabled', False);
G.CATTcpPort := JI(GObj,'cat_tcp_port', 19090);
@@ -1062,6 +1071,8 @@ begin
JW(O,'cat_slice_en_' +IntToStr(i), G.CATSliceEnabled[i]);
JW(O,'cat_slice_port_' +IntToStr(i), G.CATSlicePort[i]);
JW(O,'cat_slice_autotx_'+IntToStr(i), G.CATSliceAutoTX[i]);
JWS(O,'cat_slice_out_' +IntToStr(i), G.CATSliceOutDev[i]);
JWS(O,'cat_slice_in_' +IntToStr(i), G.CATSliceInDev[i]);
end;
JW(O,'cat_tcp_enabled',G.CATTcpEnabled);
JW(O,'cat_tcp_port',G.CATTcpPort);
@@ -1229,6 +1240,8 @@ begin
JW(O, 'slice_en_' +IntToStr(i), G.CATSliceEnabled[i]);
JW(O, 'slice_port_' +IntToStr(i), G.CATSlicePort[i]);
JW(O, 'slice_autotx_'+IntToStr(i), G.CATSliceAutoTX[i]);
JWS(O,'slice_out_' +IntToStr(i), G.CATSliceOutDev[i]);
JWS(O,'slice_in_' +IntToStr(i), G.CATSliceInDev[i]);
end;
end;
@@ -1258,6 +1271,8 @@ begin
G.CATSliceEnabled[i] := JB(O, 'slice_en_' +IntToStr(i), False);
G.CATSlicePort[i] := JI(O, 'slice_port_' +IntToStr(i), CFG_CAT_SLICE_PORT0 + i);
G.CATSliceAutoTX[i] := JB(O, 'slice_autotx_'+IntToStr(i), False);
G.CATSliceOutDev[i] := JS(O, 'slice_out_' +IntToStr(i), '');
G.CATSliceInDev[i] := JS(O, 'slice_in_' +IntToStr(i), '');
end;
end;