mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
feat(tx): TX-профиль на слайс — пилюля в шапке флага
Передатчик один, профиль до сих пор был один на устройство: активный профиль звучал независимо от того, какой слайс взял передачу. С панадаптером на FT8 и вторым на FM это значит, что компрессия и EQ голосового профиля уходили в эфир на FT8 (FMRAW прикрыт сам — ApplyTXModeSettings глушит там всю обработку, а DIGU/DIGL нет). У Flex это решается дисциплиной и внешним API; делаем в радио. Привязка «источник передачи → профиль» (-1 = не переключать, звучит активный): TCtrlSlice.TXProfile для B..G, TTXProfileList.MainBind для главного (слайс A). Персист: ключ tx_profile в слайсе пана и main_bind в секции tx_profiles. SetTxSlice зовёт ApplyBoundTXProfile ДО пуша режима и цепи, иначе ApplyTXSettingsToDSP лёг бы на старый режим. Привязали источник, который передаёт прямо сейчас, — применяем сразу. Привязки — индексы, поэтому RemapTXProfileBindings: удаление профиля из середины снимает привязки на него и сдвигает те, что правее; Factory-сброс снимает все (список заменён целиком). UI: пилюля с именем профиля в первой строке флага сразу за шириной фильтра (ЛКМ — fly-out список, первая строка «As active»). Правая граница считается до SPLIT у главного и до RXM у слайса; не рисуем при пустом списке, в DMR (передачи нет) и в FMRAW (профиль там ни на что не влияет). Прокрутка списка общая с AUD — блок стрелок вынесен в DrawScrollArrows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -178,6 +178,9 @@ type
|
||||
InDeviceIndex: Integer; // PortAudio-индекс ввода (-1 = общий)
|
||||
InDevName: string;
|
||||
AudioIn: TAudioInput; // НЕ владеет: ссылка на поток из mic-пула
|
||||
// TX-профиль слайса: индекс в FTXProfiles или -1 = «не переключать».
|
||||
// Применяется, когда слайс становится TX-источником (SetTxSlice).
|
||||
TXProfile: Integer;
|
||||
DMR: TDMRSliceDecoder; // владеет; nil для не-DMR слайса
|
||||
end;
|
||||
|
||||
@@ -596,6 +599,11 @@ type
|
||||
procedure RenameTXProfile(Idx: Integer; const AName: string);
|
||||
procedure DeleteTXProfile(Idx: Integer);
|
||||
procedure ResetTXProfilesToFactory; // заводской набор заново
|
||||
// Профиль на TX-источник: -1 = не переключать (звучит активный).
|
||||
function SliceTXProfile(SliceId: Integer): Integer;
|
||||
procedure SetSliceTXProfile(SliceId, ProfIdx: Integer);
|
||||
procedure RemapTXProfileBindings(Deleted: Integer); // после удаления/замены списка
|
||||
procedure ApplyBoundTXProfile; // профиль текущего TX-источника → в эфир
|
||||
procedure SaveTXProfilesToConfig;
|
||||
|
||||
// ---- Управление диапазонами (состояние + DSP; рендер через события) ----
|
||||
@@ -2148,6 +2156,7 @@ begin
|
||||
FSlices[slot].RxMuteOnTx := True; // себя на передаче по умолчанию не слышим
|
||||
FSlices[slot].FMSQOn := False; // squelch по умолчанию выключен
|
||||
FSlices[slot].FMSQLevel := 40;
|
||||
FSlices[slot].TXProfile := -1; // звучит тот профиль, что активен
|
||||
FSlices[slot].NRMode := 0; // DSP слайса стартует выключенным
|
||||
FSlices[slot].NBMode := 0;
|
||||
FSlices[slot].SNB := False;
|
||||
@@ -2940,6 +2949,9 @@ begin
|
||||
else if FTransmitting then SetMOX(False);
|
||||
end;
|
||||
FTxSliceId := SliceId;
|
||||
// Профиль, привязанный к новому источнику, — ДО пуша режима и цепи: иначе
|
||||
// ApplyTXSettingsToDSP внутри SelectTXProfile лёг бы на старый режим.
|
||||
ApplyBoundTXProfile;
|
||||
// Передающий тракт под режим нового источника (idle-канал, применится к PTT).
|
||||
if FWDSPReady and Assigned(FDSPEngine) then
|
||||
begin
|
||||
@@ -5033,6 +5045,9 @@ begin
|
||||
for i := Idx to FTXProfiles.Count - 2 do
|
||||
FTXProfiles.Items[i] := FTXProfiles.Items[i + 1];
|
||||
Dec(FTXProfiles.Count);
|
||||
// Привязки флагов — это ИНДЕКСЫ: после сдвига списка их надо переписать,
|
||||
// иначе слайс молча начнёт включать соседний профиль.
|
||||
RemapTXProfileBindings(Idx);
|
||||
if FTXProfiles.ActiveIdx > Idx then Dec(FTXProfiles.ActiveIdx)
|
||||
else if FTXProfiles.ActiveIdx = Idx then
|
||||
begin
|
||||
@@ -5047,6 +5062,74 @@ begin
|
||||
Changed(rfTXProfile);
|
||||
end;
|
||||
|
||||
procedure TRadioController.RemapTXProfileBindings(Deleted: Integer);
|
||||
// Профиль удалён из середины списка: привязку на него снимаем, привязки на
|
||||
// профили правее — сдвигаем. Deleted < 0 = список заменён целиком, снимаем все.
|
||||
|
||||
procedure Fix(var B: Integer);
|
||||
begin
|
||||
if B < 0 then Exit;
|
||||
if Deleted < 0 then B := -1
|
||||
else if B = Deleted then B := -1
|
||||
else if B > Deleted then Dec(B);
|
||||
end;
|
||||
|
||||
var i: Integer;
|
||||
begin
|
||||
Fix(FTXProfiles.MainBind);
|
||||
for i := 0 to High(FSlices) do
|
||||
if FSlices[i].Active then Fix(FSlices[i].TXProfile);
|
||||
end;
|
||||
|
||||
function TRadioController.SliceTXProfile(SliceId: Integer): Integer;
|
||||
// Привязка «этот источник передачи → этот профиль». -1 = не переключать.
|
||||
var idx: Integer;
|
||||
begin
|
||||
Result := -1;
|
||||
if SliceId <= 0 then
|
||||
Result := FTXProfiles.MainBind
|
||||
else
|
||||
begin
|
||||
idx := FindSliceIndex(SliceId);
|
||||
if idx >= 0 then Result := FSlices[idx].TXProfile;
|
||||
end;
|
||||
if (Result < 0) or (Result >= FTXProfiles.Count) then Result := -1;
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetSliceTXProfile(SliceId, ProfIdx: Integer);
|
||||
var idx: Integer;
|
||||
begin
|
||||
if (ProfIdx < -1) or (ProfIdx >= FTXProfiles.Count) then ProfIdx := -1;
|
||||
if SliceId <= 0 then
|
||||
begin
|
||||
if FTXProfiles.MainBind = ProfIdx then Exit;
|
||||
FTXProfiles.MainBind := ProfIdx;
|
||||
SaveTXProfilesToConfig;
|
||||
end
|
||||
else
|
||||
begin
|
||||
idx := FindSliceIndex(SliceId);
|
||||
if (idx < 0) or (FSlices[idx].TXProfile = ProfIdx) then Exit;
|
||||
FSlices[idx].TXProfile := ProfIdx;
|
||||
// Персист слайсов делает MainForm на общем сохранении панов — здесь только
|
||||
// состояние. Привязку главного пишем сразу: она живёт в секции профилей.
|
||||
end;
|
||||
// Привязали источник, который передаёт прямо сейчас, — применяем немедленно,
|
||||
// иначе профиль подхватился бы только при следующей смене TX-источника.
|
||||
if SliceId = FTxSliceId then ApplyBoundTXProfile;
|
||||
Changed(rfTXProfile);
|
||||
end;
|
||||
|
||||
procedure TRadioController.ApplyBoundTXProfile;
|
||||
// Профиль текущего TX-источника → в эфир. Ничего не делает, если источник ни к
|
||||
// чему не привязан (-1) или нужный профиль уже активен.
|
||||
var P: Integer;
|
||||
begin
|
||||
P := SliceTXProfile(FTxSliceId);
|
||||
if (P < 0) or (P = FTXProfiles.ActiveIdx) then Exit;
|
||||
SelectTXProfile(P);
|
||||
end;
|
||||
|
||||
procedure TRadioController.ResetTXProfilesToFactory;
|
||||
// Пересобирает заводской набор поверх текущего списка. База — ЖИВЫЕ настройки,
|
||||
// поэтому «Default» = микрофонный вход, полоса, динамика и мощность как сейчас,
|
||||
@@ -5055,6 +5138,7 @@ procedure TRadioController.ResetTXProfilesToFactory;
|
||||
// пересоздаётся.
|
||||
begin
|
||||
DefaultTXProfiles(FTXSettings, FDrivePercent, FTXProfiles);
|
||||
RemapTXProfileBindings(-1); // список другой — старые привязки бессмысленны
|
||||
// ActiveIdx гасим перед Select: иначе StoreActiveTXProfile внутри него
|
||||
// впишет в свежий «Default» живой EQ и сброс тембра тут же отменится.
|
||||
FTXProfiles.ActiveIdx := -1;
|
||||
|
||||
Reference in New Issue
Block a user