mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
feat(tx): привязка профиля к паре «флаг + модуляция»
Одного значения на флаг мало: на слайсе с FT8 и на нём же голосом нужен разный звук, а слайс/главный меняют модуляцию на лету. Привязка стала таблицей TTXProfModeTable (режим → индекс профиля, -1 = не переключать), своя у каждого флага: MainByMode у главного (одна на устройство, общая для КВ и трансвертера) и TXProfByMode у слайса (сама разъезжается по контекстам — записи слайсов хранятся отдельно для КВ и трансвертера). Ключ — режим САМОГО ФЛАГА (FlagMode): у главного FMode, у слайса свой. Выбор в пилюле пишется в ячейку текущего режима, поэтому смена модуляции достаёт профиль, с которым в этом виде работали в прошлый раз. Применение — в трёх точках, все ДО пуша режима в WDSP (ApplyTXChainSettings внутри SetTXMode перешьёт цепь уже новыми значениями): SetTxSlice, SetMode (если передаём с главного) и SetSliceMode (если источник — этот слайс). Персист: tx_prof_modes в записи слайса, main_prof_modes в секции tx_profiles. Чтение защитное — индекс вне текущего списка профилей читается как -1. Round-trip проверен прогоном: обе таблицы переживают запись/чтение, незаданные режимы -1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+8
-2
@@ -1570,6 +1570,10 @@ begin
|
|||||||
SyncSpecViewFreq; // иначе полоса фильтра рисуется под старый режим
|
SyncSpecViewFreq; // иначе полоса фильтра рисуется под старый режим
|
||||||
UpdateTXControlAvailability;
|
UpdateTXControlAvailability;
|
||||||
UpdateDMRStatus;
|
UpdateDMRStatus;
|
||||||
|
// Пилюля профиля во флаге A: привязка ключуется режимом, значит на
|
||||||
|
// смене модуляции она показывает уже другой профиль. Флаг главного
|
||||||
|
// приёмника живёт только на пане 0 — как в RefreshTxIndicators.
|
||||||
|
FPan.PushMainFlagExtState;
|
||||||
end;
|
end;
|
||||||
rfFilter:
|
rfFilter:
|
||||||
begin
|
begin
|
||||||
@@ -7176,7 +7180,7 @@ begin
|
|||||||
Cfg.Pans[i].Slices[n].FMSQLevel := FController.FSlices[s].FMSQLevel;
|
Cfg.Pans[i].Slices[n].FMSQLevel := FController.FSlices[s].FMSQLevel;
|
||||||
Cfg.Pans[i].Slices[n].DevName := FController.FSlices[s].DevName;
|
Cfg.Pans[i].Slices[n].DevName := FController.FSlices[s].DevName;
|
||||||
Cfg.Pans[i].Slices[n].InDevName := FController.FSlices[s].InDevName;
|
Cfg.Pans[i].Slices[n].InDevName := FController.FSlices[s].InDevName;
|
||||||
Cfg.Pans[i].Slices[n].TXProfile := FController.FSlices[s].TXProfile;
|
Cfg.Pans[i].Slices[n].TXProfByMode := FController.FSlices[s].TXProfByMode;
|
||||||
Inc(n);
|
Inc(n);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@@ -7203,7 +7207,9 @@ begin
|
|||||||
FController.FAudioIn.FindDeviceByName(SC.InDevName), SC.InDevName);
|
FController.FAudioIn.FindDeviceByName(SC.InDevName), SC.InDevName);
|
||||||
if SC.Muted then FController.SetSliceMute(Id, True);
|
if SC.Muted then FController.SetSliceMute(Id, True);
|
||||||
FController.SetSliceRxMuteOnTx(Id, SC.RxMuteOnTx);
|
FController.SetSliceRxMuteOnTx(Id, SC.RxMuteOnTx);
|
||||||
FController.SetSliceTXProfile(Id, SC.TXProfile); // «этот слайс звучит вот так»
|
// Таблица «режим → профиль» этого слайса целиком (не через SetSliceTXProfile:
|
||||||
|
// тот пишет только ячейку текущего режима).
|
||||||
|
FController.SetSliceTXProfileTable(Id, SC.TXProfByMode);
|
||||||
if SC.FMSQOn then FController.SetSliceFMSquelch(Id, True, SC.FMSQLevel);
|
if SC.FMSQOn then FController.SetSliceFMSquelch(Id, True, SC.FMSQLevel);
|
||||||
P.CreateSliceFlag(Id);
|
P.CreateSliceFlag(Id);
|
||||||
FActiveSliceId := Id;
|
FActiveSliceId := Id;
|
||||||
|
|||||||
+77
-16
@@ -178,9 +178,10 @@ type
|
|||||||
InDeviceIndex: Integer; // PortAudio-индекс ввода (-1 = общий)
|
InDeviceIndex: Integer; // PortAudio-индекс ввода (-1 = общий)
|
||||||
InDevName: string;
|
InDevName: string;
|
||||||
AudioIn: TAudioInput; // НЕ владеет: ссылка на поток из mic-пула
|
AudioIn: TAudioInput; // НЕ владеет: ссылка на поток из mic-пула
|
||||||
// TX-профиль слайса: индекс в FTXProfiles или -1 = «не переключать».
|
// TX-профиль слайса ПО РЕЖИМУ: индекс в FTXProfiles или -1 = «не
|
||||||
// Применяется, когда слайс становится TX-источником (SetTxSlice).
|
// переключать». Применяется, когда слайс становится TX-источником
|
||||||
TXProfile: Integer;
|
// (SetTxSlice) и когда он меняет режим, будучи источником.
|
||||||
|
TXProfByMode: TTXProfModeTable;
|
||||||
DMR: TDMRSliceDecoder; // владеет; nil для не-DMR слайса
|
DMR: TDMRSliceDecoder; // владеет; nil для не-DMR слайса
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -599,9 +600,14 @@ type
|
|||||||
procedure RenameTXProfile(Idx: Integer; const AName: string);
|
procedure RenameTXProfile(Idx: Integer; const AName: string);
|
||||||
procedure DeleteTXProfile(Idx: Integer);
|
procedure DeleteTXProfile(Idx: Integer);
|
||||||
procedure ResetTXProfilesToFactory; // заводской набор заново
|
procedure ResetTXProfilesToFactory; // заводской набор заново
|
||||||
// Профиль на TX-источник: -1 = не переключать (звучит активный).
|
// Профиль на TX-источник, ключ — режим этого флага (-1 = не переключать,
|
||||||
|
// звучит активный). Таблица у каждого флага своя: главный в FTXProfiles,
|
||||||
|
// слайсы в своих записях.
|
||||||
|
function FlagMode(SliceId: Integer): Integer;
|
||||||
function SliceTXProfile(SliceId: Integer): Integer;
|
function SliceTXProfile(SliceId: Integer): Integer;
|
||||||
procedure SetSliceTXProfile(SliceId, ProfIdx: Integer);
|
procedure SetSliceTXProfile(SliceId, ProfIdx: Integer);
|
||||||
|
function SliceTXProfileTable(SliceId: Integer): TTXProfModeTable;
|
||||||
|
procedure SetSliceTXProfileTable(SliceId: Integer; const T: TTXProfModeTable);
|
||||||
procedure RemapTXProfileBindings(Deleted: Integer); // после удаления/замены списка
|
procedure RemapTXProfileBindings(Deleted: Integer); // после удаления/замены списка
|
||||||
procedure ApplyBoundTXProfile; // профиль текущего TX-источника → в эфир
|
procedure ApplyBoundTXProfile; // профиль текущего TX-источника → в эфир
|
||||||
procedure SaveTXProfilesToConfig;
|
procedure SaveTXProfilesToConfig;
|
||||||
@@ -2156,7 +2162,7 @@ begin
|
|||||||
FSlices[slot].RxMuteOnTx := True; // себя на передаче по умолчанию не слышим
|
FSlices[slot].RxMuteOnTx := True; // себя на передаче по умолчанию не слышим
|
||||||
FSlices[slot].FMSQOn := False; // squelch по умолчанию выключен
|
FSlices[slot].FMSQOn := False; // squelch по умолчанию выключен
|
||||||
FSlices[slot].FMSQLevel := 40;
|
FSlices[slot].FMSQLevel := 40;
|
||||||
FSlices[slot].TXProfile := -1; // звучит тот профиль, что активен
|
ClearTXProfModeTable(FSlices[slot].TXProfByMode); // звучит активный профиль
|
||||||
FSlices[slot].NRMode := 0; // DSP слайса стартует выключенным
|
FSlices[slot].NRMode := 0; // DSP слайса стартует выключенным
|
||||||
FSlices[slot].NBMode := 0;
|
FSlices[slot].NBMode := 0;
|
||||||
FSlices[slot].SNB := False;
|
FSlices[slot].SNB := False;
|
||||||
@@ -2231,6 +2237,8 @@ begin
|
|||||||
// Этот слайс — источник передачи: передающий тракт идёт за его режимом
|
// Этот слайс — источник передачи: передающий тракт идёт за его режимом
|
||||||
// (иначе после смены режима в эфир ушёл бы прежний вид модуляции). Вход FM
|
// (иначе после смены режима в эфир ушёл бы прежний вид модуляции). Вход FM
|
||||||
// RAW — внешний PCM, поэтому заодно пересматриваем источник модуляции.
|
// RAW — внешний PCM, поэтому заодно пересматриваем источник модуляции.
|
||||||
|
// Профиль этого слайса в НОВОМ режиме — до пуша цепи (см. SetMode).
|
||||||
|
if (FTxSliceId = Id) and (Mode <> OldMode) then ApplyBoundTXProfile;
|
||||||
if (FTxSliceId = Id) and (Mode <> OldMode) and
|
if (FTxSliceId = Id) and (Mode <> OldMode) and
|
||||||
FWDSPReady and Assigned(FDSPEngine) then
|
FWDSPReady and Assigned(FDSPEngine) then
|
||||||
begin
|
begin
|
||||||
@@ -3996,6 +4004,10 @@ begin
|
|||||||
if Assigned(FDMRDec) then FDMRDec.SetEnabled(FMode = MODE_DMR);
|
if Assigned(FDMRDec) then FDMRDec.SetEnabled(FMode = MODE_DMR);
|
||||||
ApplyModeDefaults; // дефолтный фильтр/девиация под режим
|
ApplyModeDefaults; // дефолтный фильтр/девиация под режим
|
||||||
FBandCache[FCurrentBand].Mode := FMode;
|
FBandCache[FCurrentBand].Mode := FMode;
|
||||||
|
// Профиль привязан к паре «флаг + модуляция»: если передаём с главного,
|
||||||
|
// новый режим приносит свой профиль. До пуша в WDSP — ApplyTXChainSettings
|
||||||
|
// внутри SetTXMode ниже перешьёт цепь уже с новыми значениями.
|
||||||
|
if FTxSliceId = 0 then ApplyBoundTXProfile;
|
||||||
if FWDSPReady and Assigned(FDSPEngine) then
|
if FWDSPReady and Assigned(FDSPEngine) then
|
||||||
begin
|
begin
|
||||||
FDSPEngine.SetMode(FMode);
|
FDSPEngine.SetMode(FMode);
|
||||||
@@ -5074,43 +5086,61 @@ procedure TRadioController.RemapTXProfileBindings(Deleted: Integer);
|
|||||||
else if B > Deleted then Dec(B);
|
else if B > Deleted then Dec(B);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var i: Integer;
|
var i, m: Integer;
|
||||||
begin
|
begin
|
||||||
Fix(FTXProfiles.MainBind);
|
for m := 0 to CFG_MODE_MAX do Fix(FTXProfiles.MainByMode[m]);
|
||||||
for i := 0 to High(FSlices) do
|
for i := 0 to High(FSlices) do
|
||||||
if FSlices[i].Active then Fix(FSlices[i].TXProfile);
|
if FSlices[i].Active then
|
||||||
|
for m := 0 to CFG_MODE_MAX do Fix(FSlices[i].TXProfByMode[m]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TRadioController.FlagMode(SliceId: Integer): Integer;
|
||||||
|
// Режим, в котором сейчас стоит этот флаг: у главного — FMode, у слайса — свой.
|
||||||
|
var idx: Integer;
|
||||||
|
begin
|
||||||
|
Result := FMode;
|
||||||
|
if SliceId > 0 then
|
||||||
|
begin
|
||||||
|
idx := FindSliceIndex(SliceId);
|
||||||
|
if idx >= 0 then Result := FSlices[idx].Mode;
|
||||||
|
end;
|
||||||
|
Result := EnsureRange(Result, 0, CFG_MODE_MAX);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TRadioController.SliceTXProfile(SliceId: Integer): Integer;
|
function TRadioController.SliceTXProfile(SliceId: Integer): Integer;
|
||||||
// Привязка «этот источник передачи → этот профиль». -1 = не переключать.
|
// Привязка «этот источник передачи В ЭТОМ РЕЖИМЕ → этот профиль».
|
||||||
|
// -1 = не переключать. Ключ — режим самого флага, поэтому смена модуляции
|
||||||
|
// сама достаёт профиль, с которым в этом виде работали в прошлый раз.
|
||||||
var idx: Integer;
|
var idx: Integer;
|
||||||
begin
|
begin
|
||||||
Result := -1;
|
Result := -1;
|
||||||
if SliceId <= 0 then
|
if SliceId <= 0 then
|
||||||
Result := FTXProfiles.MainBind
|
Result := FTXProfiles.MainByMode[FlagMode(SliceId)]
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
idx := FindSliceIndex(SliceId);
|
idx := FindSliceIndex(SliceId);
|
||||||
if idx >= 0 then Result := FSlices[idx].TXProfile;
|
if idx >= 0 then Result := FSlices[idx].TXProfByMode[FlagMode(SliceId)];
|
||||||
end;
|
end;
|
||||||
if (Result < 0) or (Result >= FTXProfiles.Count) then Result := -1;
|
if (Result < 0) or (Result >= FTXProfiles.Count) then Result := -1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TRadioController.SetSliceTXProfile(SliceId, ProfIdx: Integer);
|
procedure TRadioController.SetSliceTXProfile(SliceId, ProfIdx: Integer);
|
||||||
var idx: Integer;
|
// Пишем в ячейку ТЕКУЩЕГО режима этого флага: выбор запоминается за модуляцией.
|
||||||
|
var idx, m: Integer;
|
||||||
begin
|
begin
|
||||||
if (ProfIdx < -1) or (ProfIdx >= FTXProfiles.Count) then ProfIdx := -1;
|
if (ProfIdx < -1) or (ProfIdx >= FTXProfiles.Count) then ProfIdx := -1;
|
||||||
|
m := FlagMode(SliceId);
|
||||||
if SliceId <= 0 then
|
if SliceId <= 0 then
|
||||||
begin
|
begin
|
||||||
if FTXProfiles.MainBind = ProfIdx then Exit;
|
if FTXProfiles.MainByMode[m] = ProfIdx then Exit;
|
||||||
FTXProfiles.MainBind := ProfIdx;
|
FTXProfiles.MainByMode[m] := ProfIdx;
|
||||||
SaveTXProfilesToConfig;
|
SaveTXProfilesToConfig;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
idx := FindSliceIndex(SliceId);
|
idx := FindSliceIndex(SliceId);
|
||||||
if (idx < 0) or (FSlices[idx].TXProfile = ProfIdx) then Exit;
|
if (idx < 0) or (FSlices[idx].TXProfByMode[m] = ProfIdx) then Exit;
|
||||||
FSlices[idx].TXProfile := ProfIdx;
|
FSlices[idx].TXProfByMode[m] := ProfIdx;
|
||||||
// Персист слайсов делает MainForm на общем сохранении панов — здесь только
|
// Персист слайсов делает MainForm на общем сохранении панов — здесь только
|
||||||
// состояние. Привязку главного пишем сразу: она живёт в секции профилей.
|
// состояние. Привязку главного пишем сразу: она живёт в секции профилей.
|
||||||
end;
|
end;
|
||||||
@@ -5120,6 +5150,37 @@ begin
|
|||||||
Changed(rfTXProfile);
|
Changed(rfTXProfile);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TRadioController.SetSliceTXProfileTable(SliceId: Integer;
|
||||||
|
const T: TTXProfModeTable);
|
||||||
|
// Восстановление сохранённой таблицы целиком (персист слайсов/панов).
|
||||||
|
var idx, m: Integer;
|
||||||
|
begin
|
||||||
|
if SliceId <= 0 then
|
||||||
|
begin
|
||||||
|
FTXProfiles.MainByMode := T;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
idx := FindSliceIndex(SliceId);
|
||||||
|
if idx < 0 then Exit;
|
||||||
|
for m := 0 to CFG_MODE_MAX do
|
||||||
|
if (T[m] >= 0) and (T[m] < FTXProfiles.Count) then
|
||||||
|
FSlices[idx].TXProfByMode[m] := T[m]
|
||||||
|
else
|
||||||
|
FSlices[idx].TXProfByMode[m] := -1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TRadioController.SliceTXProfileTable(SliceId: Integer): TTXProfModeTable;
|
||||||
|
var idx: Integer;
|
||||||
|
begin
|
||||||
|
ClearTXProfModeTable(Result);
|
||||||
|
if SliceId <= 0 then Result := FTXProfiles.MainByMode
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
idx := FindSliceIndex(SliceId);
|
||||||
|
if idx >= 0 then Result := FSlices[idx].TXProfByMode;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TRadioController.ApplyBoundTXProfile;
|
procedure TRadioController.ApplyBoundTXProfile;
|
||||||
// Профиль текущего TX-источника → в эфир. Ничего не делает, если источник ни к
|
// Профиль текущего TX-источника → в эфир. Ничего не делает, если источник ни к
|
||||||
// чему не привязан (-1) или нужный профиль уже активен.
|
// чему не привязан (-1) или нужный профиль уже активен.
|
||||||
|
|||||||
+58
-12
@@ -164,6 +164,10 @@ type
|
|||||||
// Персист панадаптеров и слайсов (секция "pans" под MAC). Восстанавливается
|
// Персист панадаптеров и слайсов (секция "pans" под MAC). Восстанавливается
|
||||||
// один раз при выходе в Run. Слайс = приёмный WDSP-канал внутри пана; частоты
|
// один раз при выходе в Run. Слайс = приёмный WDSP-канал внутри пана; частоты
|
||||||
// абсолютные (видимые). См. [[project_slices_plan]].
|
// абсолютные (видимые). См. [[project_slices_plan]].
|
||||||
|
// Привязка «режим → индекс TX-профиля» (-1 = не переключать). Такая таблица
|
||||||
|
// есть у каждого флага: у главного в TTXProfileList, у слайса в TSliceCfg.
|
||||||
|
TTXProfModeTable = array[0..CFG_MODE_MAX] of Integer;
|
||||||
|
|
||||||
TSliceCfg = record
|
TSliceCfg = record
|
||||||
TargetHz: Double; // абсолютная видимая частота слайса
|
TargetHz: Double; // абсолютная видимая частота слайса
|
||||||
Mode: Integer;
|
Mode: Integer;
|
||||||
@@ -177,7 +181,9 @@ type
|
|||||||
FMSQLevel: Integer;
|
FMSQLevel: Integer;
|
||||||
DevName: string; // имя PortAudio-устройства вывода ('' = default)
|
DevName: string; // имя PortAudio-устройства вывода ('' = default)
|
||||||
InDevName: string; // имя устройства ВВОДА слайса ('' = общий вход)
|
InDevName: string; // имя устройства ВВОДА слайса ('' = общий вход)
|
||||||
TXProfile: Integer; // TX-профиль слайса: -1 = какой активен, тот и звучит
|
// TX-профиль слайса ПО РЕЖИМУ: -1 = какой активен, тот и звучит. Таблица,
|
||||||
|
// а не одно значение: слайс на FT8 и слайс голосом помнят каждый своё.
|
||||||
|
TXProfByMode: TTXProfModeTable;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TPanCfg = record
|
TPanCfg = record
|
||||||
@@ -350,9 +356,12 @@ type
|
|||||||
TTXProfileList = record
|
TTXProfileList = record
|
||||||
Count: Integer;
|
Count: Integer;
|
||||||
ActiveIdx: Integer;
|
ActiveIdx: Integer;
|
||||||
// Привязка профиля к ГЛАВНОМУ приёмнику (слайс A). У слайсов B..G то же
|
// Привязка профиля к ГЛАВНОМУ приёмнику (слайс A) ПО РЕЖИМУ; -1 = ничего
|
||||||
// самое лежит в TSliceCfg.TXProfile; -1 = ничего не переключать.
|
// не переключать. У слайсов B..G своя такая же в TSliceCfg.TXProfByMode.
|
||||||
MainBind: Integer;
|
// Таблица главного одна на устройство — общая для КВ и трансвертера
|
||||||
|
// (решение пользователя); у слайсов она сама разъезжается по контекстам,
|
||||||
|
// потому что их записи хранятся отдельно для КВ и трансвертера.
|
||||||
|
MainByMode: TTXProfModeTable;
|
||||||
Items: array[0..TXPROF_MAX-1] of TTXProfile;
|
Items: array[0..TXPROF_MAX-1] of TTXProfile;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -701,6 +710,11 @@ procedure TXProfileApply(const P: TTXProfile; var T: TTXSettings; var DrivePct:
|
|||||||
// (иначе переключение на «ESSB» сбрасывало бы Mic In/Line In и drive).
|
// (иначе переключение на «ESSB» сбрасывало бы Mic In/Line In и drive).
|
||||||
procedure DefaultTXProfiles(const Base: TTXSettings; DrivePct: Integer;
|
procedure DefaultTXProfiles(const Base: TTXSettings; DrivePct: Integer;
|
||||||
out L: TTXProfileList);
|
out L: TTXProfileList);
|
||||||
|
// Таблица «режим → профиль» ↔ JSON-массив. Все элементы вне 0..TXPROF_MAX-1
|
||||||
|
// читаются как -1 («не переключать»), поэтому чужой/старый конфиг безопасен.
|
||||||
|
procedure ClearTXProfModeTable(var T: TTXProfModeTable);
|
||||||
|
function ModeTableToJSON(const T: TTXProfModeTable): TJSONArray;
|
||||||
|
procedure JSONToModeTable(Node: TJSONData; var T: TTXProfModeTable);
|
||||||
|
|
||||||
// Сколько пресетов реально есть у режима (VAR1/VAR2 идут сверх этого числа).
|
// Сколько пресетов реально есть у режима (VAR1/VAR2 идут сверх этого числа).
|
||||||
function FilterPresetCount(Mode: Integer): Integer;
|
function FilterPresetCount(Mode: Integer): Integer;
|
||||||
@@ -1866,6 +1880,34 @@ begin
|
|||||||
DrivePct := EnsureRange(P.DrivePercent, 0, 100);
|
DrivePct := EnsureRange(P.DrivePercent, 0, 100);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure ClearTXProfModeTable(var T: TTXProfModeTable);
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
for i := 0 to CFG_MODE_MAX do T[i] := -1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ModeTableToJSON(const T: TTXProfModeTable): TJSONArray;
|
||||||
|
var i: Integer;
|
||||||
|
begin
|
||||||
|
Result := TJSONArray.Create;
|
||||||
|
for i := 0 to CFG_MODE_MAX do Result.Add(T[i]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure JSONToModeTable(Node: TJSONData; var T: TTXProfModeTable);
|
||||||
|
var
|
||||||
|
Arr: TJSONArray;
|
||||||
|
i, V: Integer;
|
||||||
|
begin
|
||||||
|
ClearTXProfModeTable(T);
|
||||||
|
if not (Node is TJSONArray) then Exit;
|
||||||
|
Arr := TJSONArray(Node);
|
||||||
|
for i := 0 to Min(CFG_MODE_MAX, Arr.Count - 1) do
|
||||||
|
begin
|
||||||
|
V := Arr.Integers[i];
|
||||||
|
if (V >= 0) and (V < TXPROF_MAX) then T[i] := V;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure DefaultTXProfiles(const Base: TTXSettings; DrivePct: Integer;
|
procedure DefaultTXProfiles(const Base: TTXSettings; DrivePct: Integer;
|
||||||
out L: TTXProfileList);
|
out L: TTXProfileList);
|
||||||
const
|
const
|
||||||
@@ -1942,7 +1984,7 @@ begin
|
|||||||
FillChar(L, SizeOf(L), 0);
|
FillChar(L, SizeOf(L), 0);
|
||||||
L.Count := 0;
|
L.Count := 0;
|
||||||
L.ActiveIdx := 0;
|
L.ActiveIdx := 0;
|
||||||
L.MainBind := -1; // главный приёмник профиль не переключает
|
ClearTXProfModeTable(L.MainByMode); // главный ничего не переключает
|
||||||
// «Default» — текущее состояние устройства (микрофонный вход, полоса,
|
// «Default» — текущее состояние устройства (микрофонный вход, полоса,
|
||||||
// динамика, мощность), но с заводским EQ: после миграции старого конфига
|
// динамика, мощность), но с заводским EQ: после миграции старого конфига
|
||||||
// звук в эфире не меняется, а сброс к заводским возвращает ровный тембр.
|
// звук в эфире не меняется, а сброс к заводским возвращает ровный тембр.
|
||||||
@@ -1975,7 +2017,7 @@ begin
|
|||||||
FillChar(L, SizeOf(L), 0);
|
FillChar(L, SizeOf(L), 0);
|
||||||
L.Count := 0;
|
L.Count := 0;
|
||||||
L.ActiveIdx := 0;
|
L.ActiveIdx := 0;
|
||||||
L.MainBind := -1;
|
ClearTXProfModeTable(L.MainByMode);
|
||||||
Result := False;
|
Result := False;
|
||||||
MacStr := MacToStr(MAC);
|
MacStr := MacToStr(MAC);
|
||||||
if FRoot.Find(MacStr) = nil then Exit;
|
if FRoot.Find(MacStr) = nil then Exit;
|
||||||
@@ -2036,8 +2078,9 @@ begin
|
|||||||
if L.Count = 0 then Exit; // пустой список = как будто секции нет
|
if L.Count = 0 then Exit; // пустой список = как будто секции нет
|
||||||
L.ActiveIdx := JI(Root, 'active', 0);
|
L.ActiveIdx := JI(Root, 'active', 0);
|
||||||
if (L.ActiveIdx < 0) or (L.ActiveIdx >= L.Count) then L.ActiveIdx := 0;
|
if (L.ActiveIdx < 0) or (L.ActiveIdx >= L.Count) then L.ActiveIdx := 0;
|
||||||
L.MainBind := JI(Root, 'main_bind', -1);
|
JSONToModeTable(Root.Find('main_prof_modes'), L.MainByMode);
|
||||||
if (L.MainBind < -1) or (L.MainBind >= L.Count) then L.MainBind := -1;
|
for i := 0 to CFG_MODE_MAX do
|
||||||
|
if L.MainByMode[i] >= L.Count then L.MainByMode[i] := -1;
|
||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -2050,7 +2093,9 @@ var
|
|||||||
begin
|
begin
|
||||||
Root := EnsureObj(GetDevObj(MacToStr(MAC)), 'tx_profiles');
|
Root := EnsureObj(GetDevObj(MacToStr(MAC)), 'tx_profiles');
|
||||||
JW(Root, 'active', EnsureRange(L.ActiveIdx, 0, Max(0, L.Count - 1)));
|
JW(Root, 'active', EnsureRange(L.ActiveIdx, 0, Max(0, L.Count - 1)));
|
||||||
JW(Root, 'main_bind', EnsureRange(L.MainBind, -1, Max(-1, L.Count - 1)));
|
Idx := Root.IndexOfName('main_prof_modes');
|
||||||
|
if Idx >= 0 then Root.Delete(Idx);
|
||||||
|
Root.Add('main_prof_modes', ModeTableToJSON(L.MainByMode));
|
||||||
// Список пересоздаётся целиком: удаление профиля не должно оставлять хвост.
|
// Список пересоздаётся целиком: удаление профиля не должно оставлять хвост.
|
||||||
Idx := Root.IndexOfName('list');
|
Idx := Root.IndexOfName('list');
|
||||||
if Idx >= 0 then Root.Delete(Idx);
|
if Idx >= 0 then Root.Delete(Idx);
|
||||||
@@ -2741,7 +2786,8 @@ begin
|
|||||||
SliceObj.Add('fmsq_level', P.Pans[i].Slices[j].FMSQLevel);
|
SliceObj.Add('fmsq_level', P.Pans[i].Slices[j].FMSQLevel);
|
||||||
SliceObj.Add('dev_name', P.Pans[i].Slices[j].DevName);
|
SliceObj.Add('dev_name', P.Pans[i].Slices[j].DevName);
|
||||||
SliceObj.Add('in_dev_name', P.Pans[i].Slices[j].InDevName);
|
SliceObj.Add('in_dev_name', P.Pans[i].Slices[j].InDevName);
|
||||||
SliceObj.Add('tx_profile', P.Pans[i].Slices[j].TXProfile);
|
SliceObj.Add('tx_prof_modes',
|
||||||
|
ModeTableToJSON(P.Pans[i].Slices[j].TXProfByMode));
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@@ -2822,8 +2868,8 @@ begin
|
|||||||
P.Pans[id].Slices[n].FMSQLevel := JI(SliceObj, 'fmsq_level', 30);
|
P.Pans[id].Slices[n].FMSQLevel := JI(SliceObj, 'fmsq_level', 30);
|
||||||
P.Pans[id].Slices[n].DevName := JS(SliceObj, 'dev_name', '');
|
P.Pans[id].Slices[n].DevName := JS(SliceObj, 'dev_name', '');
|
||||||
P.Pans[id].Slices[n].InDevName := JS(SliceObj, 'in_dev_name', '');
|
P.Pans[id].Slices[n].InDevName := JS(SliceObj, 'in_dev_name', '');
|
||||||
P.Pans[id].Slices[n].TXProfile := EnsureRange(
|
JSONToModeTable(SliceObj.Find('tx_prof_modes'),
|
||||||
JI(SliceObj, 'tx_profile', -1), -1, TXPROF_MAX - 1);
|
P.Pans[id].Slices[n].TXProfByMode);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|||||||
Reference in New Issue
Block a user