WIP: TX implementation — per-device settings, separate analyzer, sound-card mic

- Settings: TTXSettings record + LoadTX/SaveTX, JSON section "tx" per MAC
- WDSPEngine: separate TX_DISP_ID analyzer with own FFT/Window/Det/Avg
  parameters, ApplyTXAnalyzerSettings + setters
- WDSPEngine: full TX chain setters (Filter/Compressor/Leveler/ALC/
  PhaseRot/EQ/AMCarrier/FM/CTCSS) + ApplyTXChainSettings
- WDSPEngine: ProcessTXBlock now feeds TX analyzer in DISPLAY_BLOCK_SIZE
  chunks (was passing whole FTXOut → analyzer ignored most of it)
- WDSPEngine: ActiveDisplayID switches RX/TX in UpdateSpectrum
- WDSPEngine: RX audio muted while TX active to avoid local feedback
- WDSPEngine: default EQ frequencies in Create (zero freqs broke TX chain)
- WDSPEngine: PushTXMicSampleD + OnPullMicSamples for sound-card path
- AudioInput: Available counter for non-blocking pull
- MainForm: PullSoundCardMic, BuildMicLineSelectByte, SendDUCSpecific
  with mic bias/boost/line/PTT bits, OnTXSettingsChange
- MainForm: auto-switch MicSource on TX device select, TX diagnostic
  in StatusBar panel [5]
- SettingsForm: new Transmit nav page with Microphone, Filter, Dynamic
  processing, EQ, AM/FM/CTCSS, TX Display sections

NOT YET WORKING: sound-card mic produces ring activity but signal still
not visible on TX spectrum/waterfall — needs further investigation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 08:06:58 +03:00
co-authored by Claude Opus 4.7
parent fa38e433e1
commit ce97ee486c
6 changed files with 1461 additions and 100 deletions
+483 -4
View File
@@ -27,12 +27,14 @@ uses
Classes, SysUtils, Math,
Forms, Controls, Graphics, Dialogs,
StdCtrls, ExtCtrls, Spin,
FlatButton, AudioOutput, AudioInput, AppTheme;
FlatButton, AudioOutput, AudioInput, AppTheme, Settings;
type
// PA: MaxPower в Вт, BandCal[0..10] — калибровка 38.8..100.0 на диапазон
TOnPASettingsChange = procedure(MaxPower: Double;
const BandCal: array of Double) of object;
// TX-настройки целиком (любая правка → один callback с актуальной структурой)
TOnTXSettingsChange = procedure(const T: TTXSettings) of object;
TOnDisplayParamChange = procedure(FFTSize, WindowType, SpecDetector,
SpecAvgMode: Integer; SpecAvgTimeMS: Double) of object;
@@ -66,6 +68,7 @@ type
FPagePA: TScrollBox;
FPageAdvanced: TScrollBox;
FPageCAT: TScrollBox;
FPageTransmit: TScrollBox;
FNavAudio: TFlatButton;
FNavDisplay: TFlatButton;
FNavSpectrum: TFlatButton;
@@ -73,6 +76,7 @@ type
FNavPA: TFlatButton;
FNavAdvanced: TFlatButton;
FNavCAT: TFlatButton;
FNavTransmit: TFlatButton;
// ---- Audio tab controls ----
FCmbRXDev: TComboBox;
@@ -108,6 +112,62 @@ type
FEdMaxPower: TSpinEdit;
FEdBandCal: array[0..10] of TFloatSpinEdit;
// ---- Transmit tab controls ----
// Текущее состояние, отдаётся в OnTXChange целиком при любой правке.
FTX: TTXSettings;
// Mic source / options
FCmbTXMicSource: TComboBox;
FChkTXMicBoost: TCheckBox;
FChkTXMicBias: TCheckBox;
FChkTXMicLineIn: TCheckBox;
FChkTXMicPTT: TCheckBox;
FEdTXMicGain: TFloatSpinEdit;
// Filter
FEdTXFiltLow: TSpinEdit;
FEdTXFiltHigh: TSpinEdit;
FCmbTXFiltNC: TComboBox;
FCmbTXFiltMP: TComboBox;
FCmbTXFiltWindow: TComboBox;
// Compressor
FChkTXCompOn: TCheckBox;
FEdTXCompGain: TFloatSpinEdit;
// Leveler
FChkTXLevOn: TCheckBox;
FEdTXLevTop: TFloatSpinEdit;
FEdTXLevDecay: TSpinEdit;
// ALC
FChkTXALCOn: TCheckBox;
FEdTXALCMaxGain: TFloatSpinEdit;
FEdTXALCDecay: TSpinEdit;
// Phase Rotator
FChkTXPHROn: TCheckBox;
FEdTXPHRStages: TSpinEdit;
FEdTXPHRFreq: TFloatSpinEdit;
// EQ
FChkTXEQOn: TCheckBox;
FCmbTXEQBands: TComboBox;
FEdTXEQGain: array[0..10] of TFloatSpinEdit;
FEdTXEQFreq: array[0..10] of TFloatSpinEdit;
// AM
FEdTXAMCarrier: TFloatSpinEdit;
// FM
FEdTXFMDev: TFloatSpinEdit;
FEdTXFMLow: TSpinEdit;
FEdTXFMHigh: TSpinEdit;
FCmbTXFMEmph: TComboBox;
// CTCSS
FChkTXCTCSSOn: TCheckBox;
FEdTXCTCSSFreq: TFloatSpinEdit;
// TX Display (отдельный analyzer)
FCmbTXFFT: TComboBox;
FCmbTXWindow: TComboBox;
FCmbTXSpecDet: TComboBox;
FCmbTXSpecAvg: TComboBox;
FEdTXSpecAvgTime: TFloatSpinEdit;
FCmbTXWfDet: TComboBox;
FCmbTXWfAvg: TComboBox;
FEdTXWfAvgTime: TFloatSpinEdit;
// ---- CAT tab controls ----
FCATSerialEn: array[0..3] of TCheckBox;
FCATSerialPort: array[0..3] of TEdit;
@@ -132,6 +192,7 @@ type
FOnVisibilityChange: TOnVisibilityChange;
FOnFPSChange: TOnFPSChange;
FOnThemeChange: TOnThemeChange;
FOnTXChange: TOnTXSettingsChange;
FLoading: Boolean;
@@ -141,6 +202,10 @@ type
procedure BuildRX1Tab;
procedure BuildPATab;
procedure BuildCATTab;
procedure BuildTransmitTab;
procedure CollectTXFromUI;
procedure FireTXChange;
procedure OnTXAnyChange(Sender: TObject);
procedure ApplyTheme(const T: TAppTheme);
procedure NavClick(Sender: TObject);
procedure SelectPage(APage: TWinControl; ANav: TFlatButton);
@@ -214,6 +279,7 @@ type
const SerPort: array of string;
const SerBaud, SerDataBits, SerStopBits, SerParity: array of Integer;
TcpEnabled: Boolean; TcpPort: Integer);
procedure LoadTXSettings(const T: TTXSettings);
property OnCATChange: TOnCATChange read FOnCATChange write FOnCATChange;
property OnPAChange: TOnPASettingsChange read FOnPAChange write FOnPAChange;
@@ -225,6 +291,7 @@ type
property OnVisibilityChange: TOnVisibilityChange read FOnVisibilityChange write FOnVisibilityChange;
property OnFPSChange: TOnFPSChange read FOnFPSChange write FOnFPSChange;
property OnThemeChange: TOnThemeChange read FOnThemeChange write FOnThemeChange;
property OnTXChange: TOnTXSettingsChange read FOnTXChange write FOnTXChange;
end;
implementation
@@ -429,9 +496,10 @@ begin
FNavDisplay := MakeNavButton('Display', 110);
FNavSpectrum := MakeNavButton('Spectrum', 146);
FNavWaterfall := MakeNavButton('Waterfall', 182);
FNavPA := MakeNavButton('Power Amplifier', 218);
FNavAdvanced := MakeNavButton('Advanced', 254);
FNavCAT := MakeNavButton('CAT', 290);
FNavTransmit := MakeNavButton('Transmit', 218);
FNavPA := MakeNavButton('Power Amplifier', 254);
FNavAdvanced := MakeNavButton('Advanced', 290);
FNavCAT := MakeNavButton('CAT', 326);
FContentPanel := TPanel.Create(Self);
FContentPanel.Parent := Self;
@@ -443,6 +511,7 @@ begin
FPageDisplay := MakeScrollPage;
FPageSpectrum := MakeScrollPage;
FPageWaterfall := MakeScrollPage;
FPageTransmit := MakeScrollPage;
FPagePA := MakeScrollPage;
FPageAdvanced := MakeScrollPage;
FPageCAT := MakeScrollPage;
@@ -450,6 +519,7 @@ begin
BuildAudioTab;
BuildDisplayTab;
BuildRX1Tab;
BuildTransmitTab;
BuildPATab;
BuildCATTab;
@@ -477,6 +547,7 @@ begin
else if Sender = FNavDisplay then SelectPage(FPageDisplay, FNavDisplay)
else if Sender = FNavSpectrum then SelectPage(FPageSpectrum, FNavSpectrum)
else if Sender = FNavWaterfall then SelectPage(FPageWaterfall, FNavWaterfall)
else if Sender = FNavTransmit then SelectPage(FPageTransmit, FNavTransmit)
else if Sender = FNavPA then SelectPage(FPagePA, FNavPA)
else if Sender = FNavAdvanced then SelectPage(FPageAdvanced, FNavAdvanced)
else if Sender = FNavCAT then SelectPage(FPageCAT, FNavCAT);
@@ -488,6 +559,7 @@ begin
FPageDisplay.Visible := APage = FPageDisplay;
FPageSpectrum.Visible := APage = FPageSpectrum;
FPageWaterfall.Visible := APage = FPageWaterfall;
FPageTransmit.Visible := APage = FPageTransmit;
FPagePA.Visible := APage = FPagePA;
FPageAdvanced.Visible := APage = FPageAdvanced;
FPageCAT.Visible := APage = FPageCAT;
@@ -496,6 +568,7 @@ begin
FNavDisplay.Active := ANav = FNavDisplay;
FNavSpectrum.Active := ANav = FNavSpectrum;
FNavWaterfall.Active := ANav = FNavWaterfall;
FNavTransmit.Active := ANav = FNavTransmit;
FNavPA.Active := ANav = FNavPA;
FNavAdvanced.Active := ANav = FNavAdvanced;
FNavCAT.Active := ANav = FNavCAT;
@@ -767,6 +840,412 @@ begin
FWfAGCOffset := MkFloat(CX, R1 + 2 * STEP, SPW, 0.0, -40.0, 40.0, 0.5, OnWfThresholdChange);
end;
// ---------------------------------------------------------------------------
// BuildTransmitTab — все настройки TX-тракта (по образцу Thetis Setup→Transmit)
// ---------------------------------------------------------------------------
procedure TSettingsForm.BuildTransmitTab;
const
MARGIN = 22;
PAD = 18;
LW = 150;
CX = PAD + LW + 12;
STEP = 32;
R1 = 42;
SPW = 96;
CMB_W = 200;
var
Grp: TPanel;
Y, i: Integer;
function MkSpin(AParent: TWinControl; AX, AY: Integer; AMin, AMax, AVal: Integer): TSpinEdit;
begin
Result := TSpinEdit.Create(Self);
Result.Parent := AParent;
Result.SetBounds(AX, AY, SPW, BTN_H);
Result.MinValue := AMin;
Result.MaxValue := AMax;
Result.Value := AVal;
Result.Color := CLR_INPUT;
Result.Font.Color := CLR_INPUT_TEXT;
Result.Font.Name := UI_FONT;
Result.Font.Size := 9;
Result.OnChange := OnTXAnyChange;
end;
function MkFloat(AParent: TWinControl; AX, AY: Integer;
AVal, AMin, AMax, AInc: Double): TFloatSpinEdit;
begin
Result := TFloatSpinEdit.Create(Self);
Result.Parent := AParent;
Result.SetBounds(AX, AY, SPW, BTN_H);
Result.MinValue := AMin;
Result.MaxValue := AMax;
Result.Increment := AInc;
Result.Value := AVal;
Result.DecimalPlaces := 2;
Result.Color := CLR_INPUT;
Result.Font.Color := CLR_INPUT_TEXT;
Result.Font.Name := UI_FONT;
Result.Font.Size := 9;
Result.OnChange := OnTXAnyChange;
end;
function MkChk(AParent: TWinControl; AX, AY, AW: Integer; const ACap: string): TCheckBox;
begin
Result := TCheckBox.Create(Self);
Result.Parent := AParent;
Result.Caption := ACap;
Result.SetBounds(AX, AY, AW, 22);
Result.Font.Color := CLR_TEXT;
Result.Font.Name := UI_FONT;
Result.Font.Size := 9;
Result.OnChange := OnTXAnyChange;
end;
function MkCmb(AParent: TWinControl; AX, AY, AW: Integer): TComboBox;
begin
Result := MakeCombo(AParent, AX, AY, AW, OnTXAnyChange);
end;
begin
with TLabel.Create(Self) do
begin
Parent := FPageTransmit;
Caption := 'Transmit';
SetBounds(MARGIN, 18, 400, 28);
Font.Name := UI_FONT;
Font.Size := 16;
Font.Style := [fsBold];
Font.Color := CLR_TEXT;
end;
with TLabel.Create(Self) do
begin
Parent := FPageTransmit;
Caption := 'TX-цепь WDSP: микрофон, фильтр, динамическая обработка, модуляция.';
SetBounds(MARGIN, 48, 700, 20);
Font.Name := UI_FONT;
Font.Size := 9;
Font.Color := CLR_TEXTDIM;
end;
// ---- Mic source / options ----
Y := 86;
Grp := MakeGroupPanel(FPageTransmit, 'Microphone', MARGIN, Y, 700, 220);
MakeLbl(Grp, 'Mic source', PAD, R1 + 5, LW);
FCmbTXMicSource := MkCmb(Grp, CX, R1, CMB_W);
FCmbTXMicSource.Items.Add('Radio (HW mic)');
FCmbTXMicSource.Items.Add('Sound card');
FCmbTXMicSource.ItemIndex := 0;
FChkTXMicBoost := MkChk(Grp, PAD, R1 + STEP, 200, 'Mic boost +20 dB');
FChkTXMicBias := MkChk(Grp, PAD + 200, R1 + STEP, 200, 'Mic bias');
FChkTXMicLineIn := MkChk(Grp, PAD + 400, R1 + STEP, 160, 'Line in');
FChkTXMicPTT := MkChk(Grp, PAD, R1 + 2 * STEP, 220, 'Mic-jack PTT');
MakeLbl(Grp, 'Mic gain (dB)', PAD, R1 + 3 * STEP + 5, LW);
FEdTXMicGain := MkFloat(Grp, CX, R1 + 3 * STEP, 10.0, -20.0, 60.0, 0.5);
// ---- TX bandpass filter ----
Inc(Y, 220 + 12);
Grp := MakeGroupPanel(FPageTransmit, 'TX Filter (bandpass)', MARGIN, Y, 700, 200);
MakeLbl(Grp, 'Low cut (Hz)', PAD, R1 + 5, LW);
FEdTXFiltLow := MkSpin(Grp, CX, R1, 0, 8000, 100);
MakeLbl(Grp, 'High cut (Hz)', PAD + 320, R1 + 5, 90);
FEdTXFiltHigh := MkSpin(Grp, PAD + 320 + 100, R1, 100, 12000, 2800);
MakeLbl(Grp, 'FIR length', PAD, R1 + STEP + 5, LW);
FCmbTXFiltNC := MkCmb(Grp, CX, R1 + STEP, 140);
FCmbTXFiltNC.Items.AddStrings(['256','512','1024','2048','4096']);
FCmbTXFiltNC.ItemIndex := 2;
MakeLbl(Grp, 'Phase mode', PAD, R1 + 2 * STEP + 5, LW);
FCmbTXFiltMP := MkCmb(Grp, CX, R1 + 2 * STEP, 200);
FCmbTXFiltMP.Items.AddStrings(['Linear Phase','Low Latency']);
FCmbTXFiltMP.ItemIndex := 1;
MakeLbl(Grp, 'Window', PAD, R1 + 3 * STEP + 5, LW);
FCmbTXFiltWindow := MkCmb(Grp, CX, R1 + 3 * STEP, 240);
for i := 0 to High(WINDOW_NAMES) do
FCmbTXFiltWindow.Items.Add(WINDOW_NAMES[i]);
FCmbTXFiltWindow.ItemIndex := 1;
// ---- Compressor / Leveler / ALC / Phase Rotator ----
Inc(Y, 200 + 12);
Grp := MakeGroupPanel(FPageTransmit, 'Dynamic processing', MARGIN, Y, 700, 220);
FChkTXCompOn := MkChk(Grp, PAD, R1, 220, 'Compressor');
MakeLbl(Grp, 'Gain (dB)', PAD + 230, R1 + 5, 80);
FEdTXCompGain := MkFloat(Grp, PAD + 320, R1, 5.0, 0.0, 20.0, 0.5);
FChkTXLevOn := MkChk(Grp, PAD, R1 + STEP, 220, 'Leveler');
MakeLbl(Grp, 'Top (dB)', PAD + 230, R1 + STEP + 5, 80);
FEdTXLevTop := MkFloat(Grp, PAD + 320, R1 + STEP, 5.0, 0.0, 20.0, 0.5);
MakeLbl(Grp, 'Decay (ms)', PAD + 430, R1 + STEP + 5, 80);
FEdTXLevDecay := MkSpin(Grp, PAD + 520, R1 + STEP, 1, 5000, 500);
FChkTXALCOn := MkChk(Grp, PAD, R1 + 2 * STEP, 220, 'ALC');
MakeLbl(Grp, 'Max gain', PAD + 230, R1 + 2 * STEP + 5, 80);
FEdTXALCMaxGain := MkFloat(Grp, PAD + 320, R1 + 2 * STEP, 0.0, -20.0, 20.0, 0.5);
MakeLbl(Grp, 'Decay (ms)', PAD + 430, R1 + 2 * STEP + 5, 80);
FEdTXALCDecay := MkSpin(Grp, PAD + 520, R1 + 2 * STEP, 1, 5000, 10);
FChkTXPHROn := MkChk(Grp, PAD, R1 + 3 * STEP, 220, 'Phase Rotator');
MakeLbl(Grp, 'Stages', PAD + 230, R1 + 3 * STEP + 5, 80);
FEdTXPHRStages := MkSpin(Grp, PAD + 320, R1 + 3 * STEP, 2, 16, 8);
MakeLbl(Grp, 'Corner (Hz)', PAD + 430, R1 + 3 * STEP + 5, 80);
FEdTXPHRFreq := MkFloat(Grp, PAD + 520, R1 + 3 * STEP, 338.0, 50.0, 3000.0, 1.0);
// ---- TX EQ ----
Inc(Y, 220 + 12);
Grp := MakeGroupPanel(FPageTransmit, 'TX Equalizer', MARGIN, Y, 700, 320);
FChkTXEQOn := MkChk(Grp, PAD, R1, 200, 'Enable EQ');
MakeLbl(Grp, 'Bands', PAD + 230, R1 + 5, 60);
FCmbTXEQBands := MkCmb(Grp, PAD + 290, R1, 80);
FCmbTXEQBands.Items.AddStrings(['3','10']);
FCmbTXEQBands.ItemIndex := 1;
// 11 строк: [0]=Preamp, [1..10]=полосы. Freq+Gain в одной строке.
for i := 0 to 10 do
begin
if i = 0 then
MakeLbl(Grp, 'Preamp (dB)', PAD, R1 + STEP + i * 22 + 3, 100)
else
MakeLbl(Grp, Format('Band %d', [i]), PAD, R1 + STEP + i * 22 + 3, 80);
FEdTXEQFreq[i] := MkFloat(Grp, PAD + 90, R1 + STEP + i * 22, 0.0, 0.0, 20000.0, 10.0);
FEdTXEQFreq[i].Width := 100;
MakeLbl(Grp, 'gain', PAD + 200, R1 + STEP + i * 22 + 3, 30);
FEdTXEQGain[i] := MkFloat(Grp, PAD + 230, R1 + STEP + i * 22, 0.0, -15.0, 15.0, 0.5);
FEdTXEQGain[i].Width := 80;
end;
// ---- AM / FM / CTCSS ----
Inc(Y, 320 + 12);
Grp := MakeGroupPanel(FPageTransmit, 'AM / FM / CTCSS', MARGIN, Y, 700, 220);
MakeLbl(Grp, 'AM carrier level', PAD, R1 + 5, LW);
FEdTXAMCarrier := MkFloat(Grp, CX, R1, 0.5, 0.0, 1.0, 0.05);
MakeLbl(Grp, 'FM deviation (Hz)', PAD, R1 + STEP + 5, LW);
FEdTXFMDev := MkFloat(Grp, CX, R1 + STEP, 5000.0, 1000.0, 25000.0, 100.0);
MakeLbl(Grp, 'FM AF low cut', PAD, R1 + 2 * STEP + 5, LW);
FEdTXFMLow := MkSpin(Grp, CX, R1 + 2 * STEP, 0, 5000, 300);
MakeLbl(Grp, 'FM AF high cut', PAD + 320, R1 + 2 * STEP + 5, 100);
FEdTXFMHigh := MkSpin(Grp, PAD + 320 + 110, R1 + 2 * STEP, 100, 8000, 3000);
MakeLbl(Grp, 'FM emphasis', PAD, R1 + 3 * STEP + 5, LW);
FCmbTXFMEmph := MkCmb(Grp, CX, R1 + 3 * STEP, 180);
FCmbTXFMEmph.Items.AddStrings(['Pre-EQ','Post-EQ']);
FCmbTXFMEmph.ItemIndex := 1;
FChkTXCTCSSOn := MkChk(Grp, PAD, R1 + 4 * STEP, 220, 'CTCSS tone');
MakeLbl(Grp, 'Freq (Hz)', PAD + 230, R1 + 4 * STEP + 5, 80);
FEdTXCTCSSFreq := MkFloat(Grp, PAD + 320, R1 + 4 * STEP, 100.0, 50.0, 300.0, 0.1);
// ---- TX Display (отдельный analyzer для TX-спектра/водопада) ----
Inc(Y, 220 + 12);
Grp := MakeGroupPanel(FPageTransmit, 'TX Display (Analyzer)', MARGIN, Y, 700, 290);
MakeLbl(Grp, 'FFT size', PAD, R1 + 5, LW);
FCmbTXFFT := MkCmb(Grp, CX, R1, 140);
for i := 0 to High(FFT_NAMES) do FCmbTXFFT.Items.Add(FFT_NAMES[i]);
FCmbTXFFT.ItemIndex := 2;
MakeLbl(Grp, 'Window', PAD, R1 + STEP + 5, LW);
FCmbTXWindow := MkCmb(Grp, CX, R1 + STEP, 240);
for i := 0 to High(WINDOW_NAMES) do FCmbTXWindow.Items.Add(WINDOW_NAMES[i]);
FCmbTXWindow.ItemIndex := 2;
MakeLbl(Grp, 'Spec detector',PAD, R1 + 2 * STEP + 5, LW);
FCmbTXSpecDet := MkCmb(Grp, CX, R1 + 2 * STEP, 200);
for i := 0 to High(DETECTOR_NAMES) do FCmbTXSpecDet.Items.Add(DETECTOR_NAMES[i]);
FCmbTXSpecDet.ItemIndex := 0;
MakeLbl(Grp, 'Spec average', PAD, R1 + 3 * STEP + 5, LW);
FCmbTXSpecAvg := MkCmb(Grp, CX, R1 + 3 * STEP, 200);
for i := 0 to High(AVGMODE_NAMES) do FCmbTXSpecAvg.Items.Add(AVGMODE_NAMES[i]);
FCmbTXSpecAvg.ItemIndex := 3;
MakeLbl(Grp, 'Avg time (ms)', PAD + 430, R1 + 3 * STEP + 5, 100);
FEdTXSpecAvgTime := MkFloat(Grp, PAD + 540, R1 + 3 * STEP, 30.0, 1.0, 5000.0, 1.0);
MakeLbl(Grp, 'WF detector', PAD, R1 + 4 * STEP + 5, LW);
FCmbTXWfDet := MkCmb(Grp, CX, R1 + 4 * STEP, 200);
for i := 0 to High(DETECTOR_NAMES) do FCmbTXWfDet.Items.Add(DETECTOR_NAMES[i]);
FCmbTXWfDet.ItemIndex := 0;
MakeLbl(Grp, 'WF average', PAD, R1 + 5 * STEP + 5, LW);
FCmbTXWfAvg := MkCmb(Grp, CX, R1 + 5 * STEP, 200);
for i := 0 to High(WF_AVGMODE_NAMES) do FCmbTXWfAvg.Items.Add(WF_AVGMODE_NAMES[i]);
FCmbTXWfAvg.ItemIndex := 3;
MakeLbl(Grp, 'Avg time (ms)', PAD + 430, R1 + 5 * STEP + 5, 100);
FEdTXWfAvgTime := MkFloat(Grp, PAD + 540, R1 + 5 * STEP, 120.0, 1.0, 5000.0, 1.0);
end;
procedure TSettingsForm.CollectTXFromUI;
var i: Integer;
begin
if FCmbTXMicSource <> nil then
FTX.MicSource := Max(0, FCmbTXMicSource.ItemIndex);
if FChkTXMicBoost <> nil then FTX.MicBoost := FChkTXMicBoost.Checked;
if FChkTXMicBias <> nil then FTX.MicBias := FChkTXMicBias.Checked;
if FChkTXMicLineIn <> nil then FTX.MicLineIn := FChkTXMicLineIn.Checked;
if FChkTXMicPTT <> nil then FTX.MicPTTEnabled := FChkTXMicPTT.Checked;
if FEdTXMicGain <> nil then FTX.MicGainDB := FEdTXMicGain.Value;
if FEdTXFiltLow <> nil then FTX.FilterLow := FEdTXFiltLow.Value;
if FEdTXFiltHigh <> nil then FTX.FilterHigh := FEdTXFiltHigh.Value;
if FCmbTXFiltNC <> nil then
FTX.FilterNC := StrToIntDef(FCmbTXFiltNC.Text, 1024);
if FCmbTXFiltMP <> nil then FTX.FilterMP := Max(0, FCmbTXFiltMP.ItemIndex);
if FCmbTXFiltWindow<> nil then FTX.FilterWindow := Max(0, FCmbTXFiltWindow.ItemIndex);
if FChkTXCompOn <> nil then FTX.CompressorOn := FChkTXCompOn.Checked;
if FEdTXCompGain <> nil then FTX.CompressorGain := FEdTXCompGain.Value;
if FChkTXLevOn <> nil then FTX.LevelerOn := FChkTXLevOn.Checked;
if FEdTXLevTop <> nil then FTX.LevelerTop := FEdTXLevTop.Value;
if FEdTXLevDecay <> nil then FTX.LevelerDecay := FEdTXLevDecay.Value;
if FChkTXALCOn <> nil then FTX.ALCOn := FChkTXALCOn.Checked;
if FEdTXALCMaxGain <> nil then FTX.ALCMaxGain := FEdTXALCMaxGain.Value;
if FEdTXALCDecay <> nil then FTX.ALCDecay := FEdTXALCDecay.Value;
if FChkTXPHROn <> nil then FTX.PhaseRotOn := FChkTXPHROn.Checked;
if FEdTXPHRStages <> nil then FTX.PhaseRotStages := FEdTXPHRStages.Value;
if FEdTXPHRFreq <> nil then FTX.PhaseRotFreq := FEdTXPHRFreq.Value;
if FChkTXEQOn <> nil then FTX.EQOn := FChkTXEQOn.Checked;
if FCmbTXEQBands <> nil then
if FCmbTXEQBands.ItemIndex = 0 then FTX.EQNumBands := 3 else FTX.EQNumBands := 10;
for i := 0 to 10 do
begin
if FEdTXEQGain[i] <> nil then FTX.EQGains[i] := FEdTXEQGain[i].Value;
if FEdTXEQFreq[i] <> nil then FTX.EQFreqs[i] := FEdTXEQFreq[i].Value;
end;
if FEdTXAMCarrier <> nil then FTX.AMCarrierLevel := FEdTXAMCarrier.Value;
if FEdTXFMDev <> nil then FTX.FMDeviation := FEdTXFMDev.Value;
if FEdTXFMLow <> nil then FTX.FMLowCut := FEdTXFMLow.Value;
if FEdTXFMHigh <> nil then FTX.FMHighCut := FEdTXFMHigh.Value;
if FCmbTXFMEmph <> nil then FTX.FMEmphPosition := Max(0, FCmbTXFMEmph.ItemIndex);
if FChkTXCTCSSOn <> nil then FTX.CTCSSOn := FChkTXCTCSSOn.Checked;
if FEdTXCTCSSFreq <> nil then FTX.CTCSSFreq := FEdTXCTCSSFreq.Value;
if (FCmbTXFFT <> nil) and (FCmbTXFFT.ItemIndex >= 0) then
FTX.TXFFTSize := FFT_SIZES[FCmbTXFFT.ItemIndex];
if FCmbTXWindow <> nil then FTX.TXWindowType := Max(0, FCmbTXWindow.ItemIndex);
if FCmbTXSpecDet <> nil then FTX.TXSpecDetector := Max(0, FCmbTXSpecDet.ItemIndex);
if (FCmbTXSpecAvg <> nil) and (FCmbTXSpecAvg.ItemIndex >= 0) then
FTX.TXSpecAvgMode := AVGMODE_VALUES[FCmbTXSpecAvg.ItemIndex];
if FEdTXSpecAvgTime <> nil then FTX.TXSpecAvgTimeMS := FEdTXSpecAvgTime.Value;
if FCmbTXWfDet <> nil then FTX.TXWfDetector := Max(0, FCmbTXWfDet.ItemIndex);
if (FCmbTXWfAvg <> nil) and (FCmbTXWfAvg.ItemIndex >= 0) then
FTX.TXWfAvgMode := WF_AVGMODE_VALUES[FCmbTXWfAvg.ItemIndex];
if FEdTXWfAvgTime <> nil then FTX.TXWfAvgTimeMS := FEdTXWfAvgTime.Value;
end;
procedure TSettingsForm.FireTXChange;
begin
if FLoading then Exit;
CollectTXFromUI;
if Assigned(FOnTXChange) then
FOnTXChange(FTX);
end;
procedure TSettingsForm.OnTXAnyChange(Sender: TObject);
begin
FireTXChange;
end;
procedure TSettingsForm.LoadTXSettings(const T: TTXSettings);
var i: Integer;
begin
FLoading := True;
try
FTX := T;
if FCmbTXMicSource <> nil then
FCmbTXMicSource.ItemIndex := EnsureRange(T.MicSource, 0, FCmbTXMicSource.Items.Count - 1);
if FChkTXMicBoost <> nil then FChkTXMicBoost.Checked := T.MicBoost;
if FChkTXMicBias <> nil then FChkTXMicBias.Checked := T.MicBias;
if FChkTXMicLineIn <> nil then FChkTXMicLineIn.Checked := T.MicLineIn;
if FChkTXMicPTT <> nil then FChkTXMicPTT.Checked := T.MicPTTEnabled;
if FEdTXMicGain <> nil then FEdTXMicGain.Value := T.MicGainDB;
if FEdTXFiltLow <> nil then FEdTXFiltLow.Value := T.FilterLow;
if FEdTXFiltHigh <> nil then FEdTXFiltHigh.Value := T.FilterHigh;
if FCmbTXFiltNC <> nil then FCmbTXFiltNC.Text := IntToStr(T.FilterNC);
if FCmbTXFiltMP <> nil then
FCmbTXFiltMP.ItemIndex := EnsureRange(T.FilterMP, 0, FCmbTXFiltMP.Items.Count - 1);
if FCmbTXFiltWindow<> nil then
FCmbTXFiltWindow.ItemIndex := EnsureRange(T.FilterWindow, 0, FCmbTXFiltWindow.Items.Count - 1);
if FChkTXCompOn <> nil then FChkTXCompOn.Checked := T.CompressorOn;
if FEdTXCompGain <> nil then FEdTXCompGain.Value := T.CompressorGain;
if FChkTXLevOn <> nil then FChkTXLevOn.Checked := T.LevelerOn;
if FEdTXLevTop <> nil then FEdTXLevTop.Value := T.LevelerTop;
if FEdTXLevDecay <> nil then FEdTXLevDecay.Value := T.LevelerDecay;
if FChkTXALCOn <> nil then FChkTXALCOn.Checked := T.ALCOn;
if FEdTXALCMaxGain <> nil then FEdTXALCMaxGain.Value := T.ALCMaxGain;
if FEdTXALCDecay <> nil then FEdTXALCDecay.Value := T.ALCDecay;
if FChkTXPHROn <> nil then FChkTXPHROn.Checked := T.PhaseRotOn;
if FEdTXPHRStages <> nil then FEdTXPHRStages.Value := T.PhaseRotStages;
if FEdTXPHRFreq <> nil then FEdTXPHRFreq.Value := T.PhaseRotFreq;
if FChkTXEQOn <> nil then FChkTXEQOn.Checked := T.EQOn;
if FCmbTXEQBands <> nil then
if T.EQNumBands <= 3 then FCmbTXEQBands.ItemIndex := 0
else FCmbTXEQBands.ItemIndex := 1;
for i := 0 to 10 do
begin
if FEdTXEQGain[i] <> nil then FEdTXEQGain[i].Value := T.EQGains[i];
if FEdTXEQFreq[i] <> nil then FEdTXEQFreq[i].Value := T.EQFreqs[i];
end;
if FEdTXAMCarrier <> nil then FEdTXAMCarrier.Value := T.AMCarrierLevel;
if FEdTXFMDev <> nil then FEdTXFMDev.Value := T.FMDeviation;
if FEdTXFMLow <> nil then FEdTXFMLow.Value := T.FMLowCut;
if FEdTXFMHigh <> nil then FEdTXFMHigh.Value := T.FMHighCut;
if FCmbTXFMEmph <> nil then
FCmbTXFMEmph.ItemIndex := EnsureRange(T.FMEmphPosition, 0, FCmbTXFMEmph.Items.Count - 1);
if FChkTXCTCSSOn <> nil then FChkTXCTCSSOn.Checked := T.CTCSSOn;
if FEdTXCTCSSFreq <> nil then FEdTXCTCSSFreq.Value := T.CTCSSFreq;
if FCmbTXFFT <> nil then
begin
FCmbTXFFT.ItemIndex := 2;
for i := 0 to High(FFT_SIZES) do
if FFT_SIZES[i] = T.TXFFTSize then
begin
FCmbTXFFT.ItemIndex := i;
Break;
end;
end;
if FCmbTXWindow <> nil then
FCmbTXWindow.ItemIndex := EnsureRange(T.TXWindowType, 0, FCmbTXWindow.Items.Count - 1);
if FCmbTXSpecDet <> nil then
FCmbTXSpecDet.ItemIndex := EnsureRange(T.TXSpecDetector, 0, FCmbTXSpecDet.Items.Count - 1);
if FCmbTXSpecAvg <> nil then
begin
FCmbTXSpecAvg.ItemIndex := 3;
for i := 0 to High(AVGMODE_VALUES) do
if AVGMODE_VALUES[i] = T.TXSpecAvgMode then
begin
FCmbTXSpecAvg.ItemIndex := i;
Break;
end;
end;
if FEdTXSpecAvgTime <> nil then FEdTXSpecAvgTime.Value := T.TXSpecAvgTimeMS;
if FCmbTXWfDet <> nil then
FCmbTXWfDet.ItemIndex := EnsureRange(T.TXWfDetector, 0, FCmbTXWfDet.Items.Count - 1);
if FCmbTXWfAvg <> nil then
begin
FCmbTXWfAvg.ItemIndex := 3;
for i := 0 to High(WF_AVGMODE_VALUES) do
if WF_AVGMODE_VALUES[i] = T.TXWfAvgMode then
begin
FCmbTXWfAvg.ItemIndex := i;
Break;
end;
end;
if FEdTXWfAvgTime <> nil then FEdTXWfAvgTime.Value := T.TXWfAvgTimeMS;
finally
FLoading := False;
end;
end;
// ---------------------------------------------------------------------------
// BuildPATab
// ---------------------------------------------------------------------------