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
+52 -3
View File
@@ -581,7 +581,15 @@ type
procedure OnSliceCATSettingsChange(
const Enabled: array of Boolean;
const Port: array of Integer;
const AutoTX: array of Boolean);
const AutoTX: array of Boolean;
const OutDev: array of string;
const InDev: array of string);
// Выбор устройства во флаге слайса = устройство его слота: зеркалим в
// конфиг, чтобы вкладка Slices показывала то же самое.
procedure StoreSliceSlotAudio(SliceId: Integer);
// Аудио слотов из конфига → контроллер (старт и смена устройства): слайсы
// восстанавливаются позже и должны встать на свои звуковухи.
procedure ApplySliceSlotAudio;
procedure OnAlexSettingsChange(const A: TAlexSettings);
procedure OnXvtrSettingsChange(const X: TXvtrSettings);
procedure PushXvtrToWeb;
@@ -1127,6 +1135,7 @@ begin
FLightTheme := FController.FSettings.LoadTheme;
FCATAdapter := TCATAdapter.Create(FController);
FCATAdapter.ApplySettings(FCATLastGlobal);
ApplySliceSlotAudio;
FSMeterPeak := -130;
FSMeterMin := -130;
FSMeterAvg := -130;
@@ -1679,6 +1688,7 @@ begin
// возвращаем живое значение в persist-буфер, чтобы blob нёс актуальную тему.
FController.FLoadedGlobal.LightTheme := FLightTheme;
FCATAdapter.ApplySettings(FController.FLoadedGlobal);
ApplySliceSlotAudio;
RelabelBands; // HF ↔ VHF/UHF подписи под тип бэкенда
ApplyBackendCapsToUI; // пресеты sample rate + гейтинг полей
RebuildXvtrButtons;
@@ -5834,6 +5844,7 @@ begin
if CurSliceId <> 0 then
begin
FController.SetSliceDevice(CurSliceId, DevIndex, DevName);
StoreSliceSlotAudio(CurSliceId); // вкладка Slices показывает то же устройство
PushFlagStateAllPans(CurSliceId); // обновить подсветку текущего устройства слайса
Exit;
end;
@@ -5848,6 +5859,7 @@ begin
if CurSliceId <> 0 then
begin
FController.SetSliceInDevice(CurSliceId, DevIndex, DevName);
StoreSliceSlotAudio(CurSliceId);
PushFlagStateAllPans(CurSliceId);
Exit;
end;
@@ -8516,7 +8528,9 @@ begin
SF.LoadSliceCATSettings(
FCATLastGlobal.CATSliceEnabled,
FCATLastGlobal.CATSlicePort,
FCATLastGlobal.CATSliceAutoTX);
FCATLastGlobal.CATSliceAutoTX,
FCATLastGlobal.CATSliceOutDev,
FCATLastGlobal.CATSliceInDev);
// Перечисляем PA устройства
SF.RefreshAudioDevices(FController.FAudioOut, FController.FAudioIn);
SF.LoadVisibility(FController.FShowSpectrum, FController.FShowWaterfall, FController.FShowWideband, FController.FWidebandFill,
@@ -8622,7 +8636,9 @@ end;
procedure TMainForm.OnSliceCATSettingsChange(
const Enabled: array of Boolean;
const Port: array of Integer;
const AutoTX: array of Boolean);
const AutoTX: array of Boolean;
const OutDev: array of string;
const InDev: array of string);
// CAT дополнительных слайсов. Хранится там же, где остальные CAT-настройки
// (корневая секция + device-blob), применяется тем же ApplySettings: адаптер
// поднимет/опустит порты эндпоинтов и положит Auto TX в контроллер.
@@ -8633,15 +8649,48 @@ begin
if i <= High(Enabled) then FCATLastGlobal.CATSliceEnabled[i] := Enabled[i];
if i <= High(Port) then FCATLastGlobal.CATSlicePort[i] := Port[i];
if i <= High(AutoTX) then FCATLastGlobal.CATSliceAutoTX[i] := AutoTX[i];
if i <= High(OutDev) then FCATLastGlobal.CATSliceOutDev[i] := OutDev[i];
if i <= High(InDev) then FCATLastGlobal.CATSliceInDev[i] := InDev[i];
// Аудио слота — сразу в контроллер: слайс, стоящий на слоте, переезжает
// на новое устройство, следующий получит его при создании.
FController.SetSliceSlotAudio(i, FCATLastGlobal.CATSliceOutDev[i],
FCATLastGlobal.CATSliceInDev[i]);
end;
FController.FLoadedGlobal.CATSliceEnabled := FCATLastGlobal.CATSliceEnabled;
FController.FLoadedGlobal.CATSlicePort := FCATLastGlobal.CATSlicePort;
FController.FLoadedGlobal.CATSliceAutoTX := FCATLastGlobal.CATSliceAutoTX;
FController.FLoadedGlobal.CATSliceOutDev := FCATLastGlobal.CATSliceOutDev;
FController.FLoadedGlobal.CATSliceInDev := FCATLastGlobal.CATSliceInDev;
FCATAdapter.ApplySettings(FCATLastGlobal);
FController.FSettings.SaveCATSettings(FCATLastGlobal);
FController.SaveGlobalSettings;
FController.FSettings.Save;
RefreshTxIndicators; // бейджи AutoTX на флагах слайсов — сразу
for i := 0 to MAX_PANS - 1 do // подсветка текущего устройства во флагах
if FPans[i] <> nil then FPans[i].PushAllSliceFlagStates;
end;
procedure TMainForm.ApplySliceSlotAudio;
var i: Integer;
begin
for i := 0 to CFG_CAT_SLICE_COUNT - 1 do
FController.SetSliceSlotAudio(i, FCATLastGlobal.CATSliceOutDev[i],
FCATLastGlobal.CATSliceInDev[i]);
end;
procedure TMainForm.StoreSliceSlotAudio(SliceId: Integer);
// Слот слайса и его устройства уже обновлены контроллером (SetSliceDevice/
// SetSliceInDevice) — здесь только зеркалим их в конфиг и сохраняем.
var Slot: Integer;
begin
Slot := FController.SliceSlotOf(SliceId);
if (Slot < 0) or (Slot >= CFG_CAT_SLICE_COUNT) then Exit;
FCATLastGlobal.CATSliceOutDev[Slot] := FController.SliceSlotOutDev(Slot);
FCATLastGlobal.CATSliceInDev[Slot] := FController.SliceSlotInDev(Slot);
FController.FLoadedGlobal.CATSliceOutDev := FCATLastGlobal.CATSliceOutDev;
FController.FLoadedGlobal.CATSliceInDev := FCATLastGlobal.CATSliceInDev;
FController.FSettings.SaveCATSettings(FCATLastGlobal);
FController.FSettings.Save;
end;
end.