mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-27 02:37:32 +00:00
Add audio buffer setting and optimize overlays
This commit is contained in:
+15
-3
@@ -10,7 +10,7 @@ unit AudioOutput;
|
|||||||
- Callback читает по одному сэмплу, обновляет outpt внутри цикла
|
- Callback читает по одному сэмплу, обновляет outpt внутри цикла
|
||||||
- Low water mark: вставляет тишину + полбуфера silence
|
- Low water mark: вставляет тишину + полбуфера silence
|
||||||
- High water mark: удаляет лишние сэмплы
|
- High water mark: удаляет лишние сэмплы
|
||||||
- MY_AUDIO_BUFFER_SIZE = 128 frames (низкая латентность)
|
- output buffer size defaults to 128 frames (низкая латентность)
|
||||||
}
|
}
|
||||||
|
|
||||||
{$IFDEF FPC}
|
{$IFDEF FPC}
|
||||||
@@ -24,7 +24,7 @@ uses
|
|||||||
Classes, SysUtils, Math, SyncObjs, DynLibs;
|
Classes, SysUtils, Math, SyncObjs, DynLibs;
|
||||||
|
|
||||||
const
|
const
|
||||||
MY_AUDIO_BUFFER_SIZE = 128; // PA frames per callback — как piHPSDR
|
MY_AUDIO_BUFFER_SIZE = 128; // default PA frames per callback — как piHPSDR
|
||||||
MY_RING_BUFFER_SIZE = 9600; // как piHPSDR
|
MY_RING_BUFFER_SIZE = 9600; // как piHPSDR
|
||||||
MY_RING_LOW_WATER = 512; // как piHPSDR
|
MY_RING_LOW_WATER = 512; // как piHPSDR
|
||||||
MY_RING_HIGH_WATER = 9000; // как piHPSDR
|
MY_RING_HIGH_WATER = 9000; // как piHPSDR
|
||||||
@@ -95,6 +95,7 @@ type
|
|||||||
FLibHandle: TLibHandle;
|
FLibHandle: TLibHandle;
|
||||||
FStream: TPaStream;
|
FStream: TPaStream;
|
||||||
FSampleRate: Integer;
|
FSampleRate: Integer;
|
||||||
|
FOutputBufferSize: Integer;
|
||||||
FOpen: Boolean;
|
FOpen: Boolean;
|
||||||
FPAInited: Boolean; // Pa_Initialize прошла
|
FPAInited: Boolean; // Pa_Initialize прошла
|
||||||
FLastError: string;
|
FLastError: string;
|
||||||
@@ -120,6 +121,7 @@ type
|
|||||||
|
|
||||||
function LoadLib: Boolean;
|
function LoadLib: Boolean;
|
||||||
function GetLastError: string;
|
function GetLastError: string;
|
||||||
|
procedure SetOutputBufferSize(V: Integer);
|
||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(SampleRate: Integer = 48000);
|
constructor Create(SampleRate: Integer = 48000);
|
||||||
@@ -148,6 +150,7 @@ type
|
|||||||
property LastError: string read GetLastError;
|
property LastError: string read GetLastError;
|
||||||
property DeviceIndex: Integer read FDeviceIndex write FDeviceIndex;
|
property DeviceIndex: Integer read FDeviceIndex write FDeviceIndex;
|
||||||
property SampleRate: Integer read FSampleRate write FSampleRate;
|
property SampleRate: Integer read FSampleRate write FSampleRate;
|
||||||
|
property OutputBufferSize: Integer read FOutputBufferSize write SetOutputBufferSize;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Глобальный callback (cdecl, не метод)
|
// Глобальный callback (cdecl, не метод)
|
||||||
@@ -239,6 +242,7 @@ constructor TAudioOutput.Create(SampleRate: Integer);
|
|||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FSampleRate := SampleRate;
|
FSampleRate := SampleRate;
|
||||||
|
FOutputBufferSize := MY_AUDIO_BUFFER_SIZE;
|
||||||
FDeviceIndex := -1; // -1 = default output device
|
FDeviceIndex := -1; // -1 = default output device
|
||||||
FOpen := False;
|
FOpen := False;
|
||||||
FPAInited := False;
|
FPAInited := False;
|
||||||
@@ -493,7 +497,7 @@ begin
|
|||||||
nil, // no input
|
nil, // no input
|
||||||
@OutParam,
|
@OutParam,
|
||||||
FSampleRate,
|
FSampleRate,
|
||||||
MY_AUDIO_BUFFER_SIZE, // 128 frames как в оригинале
|
FOutputBufferSize,
|
||||||
PA_NO_FLAG,
|
PA_NO_FLAG,
|
||||||
@PaOutCallback,
|
@PaOutCallback,
|
||||||
Self
|
Self
|
||||||
@@ -531,6 +535,14 @@ begin
|
|||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TAudioOutput.SetOutputBufferSize(V: Integer);
|
||||||
|
begin
|
||||||
|
if V <= 128 then V := 128
|
||||||
|
else if V <= 256 then V := 256
|
||||||
|
else V := 512;
|
||||||
|
FOutputBufferSize := V;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TAudioOutput.Close;
|
procedure TAudioOutput.Close;
|
||||||
begin
|
begin
|
||||||
if not FOpen then Exit;
|
if not FOpen then Exit;
|
||||||
|
|||||||
+80
-19
@@ -660,6 +660,7 @@ type
|
|||||||
procedure OnModeFilterSelect(Mode: Integer; FilterBW: Integer);
|
procedure OnModeFilterSelect(Mode: Integer; FilterBW: Integer);
|
||||||
procedure OnVfoOverlayDSPChange(NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean);
|
procedure OnVfoOverlayDSPChange(NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean);
|
||||||
procedure OnVfoOverlayAGCChange(AGCMode: Integer);
|
procedure OnVfoOverlayAGCChange(AGCMode: Integer);
|
||||||
|
procedure OnVfoOverlayInvalidate(Sender: TObject);
|
||||||
procedure PbSpectrumDblClick(Sender: TObject);
|
procedure PbSpectrumDblClick(Sender: TObject);
|
||||||
procedure TrkDriveChange(Sender: TObject);
|
procedure TrkDriveChange(Sender: TObject);
|
||||||
procedure TrkVolumeChange(Sender: TObject);
|
procedure TrkVolumeChange(Sender: TObject);
|
||||||
@@ -702,6 +703,7 @@ type
|
|||||||
procedure ApplySpecViewGridFromState;
|
procedure ApplySpecViewGridFromState;
|
||||||
procedure ApplyAudioDevice(DevIndex: Integer; const DevName: string);
|
procedure ApplyAudioDevice(DevIndex: Integer; const DevName: string);
|
||||||
procedure ApplyAudioInputDevice(DevIndex: Integer; const DevName: string);
|
procedure ApplyAudioInputDevice(DevIndex: Integer; const DevName: string);
|
||||||
|
procedure ApplyAudioBufferSize(BufferSize: Integer);
|
||||||
// TX settings: применить FTXSettings к WDSP и переслать DUC Specific
|
// TX settings: применить FTXSettings к WDSP и переслать DUC Specific
|
||||||
// (mic-биты Boost/Bias/LineIn/PTT попадают в byte 50 DUCSpecific).
|
// (mic-биты Boost/Bias/LineIn/PTT попадают в byte 50 DUCSpecific).
|
||||||
procedure ApplyTXSettingsToDSP;
|
procedure ApplyTXSettingsToDSP;
|
||||||
@@ -1321,6 +1323,7 @@ begin
|
|||||||
FVfoOverlay.OnSelect := OnModeFilterSelect;
|
FVfoOverlay.OnSelect := OnModeFilterSelect;
|
||||||
FVfoOverlay.OnDSPChange := OnVfoOverlayDSPChange;
|
FVfoOverlay.OnDSPChange := OnVfoOverlayDSPChange;
|
||||||
FVfoOverlay.OnAGCChange := OnVfoOverlayAGCChange;
|
FVfoOverlay.OnAGCChange := OnVfoOverlayAGCChange;
|
||||||
|
FVfoOverlay.OnInvalidate := OnVfoOverlayInvalidate;
|
||||||
FVfoOverlay.Visible := False;
|
FVfoOverlay.Visible := False;
|
||||||
FPanelHidden := False;
|
FPanelHidden := False;
|
||||||
FShowSpectrum := True;
|
FShowSpectrum := True;
|
||||||
@@ -1331,6 +1334,7 @@ begin
|
|||||||
FSplitterDrag := False;
|
FSplitterDrag := False;
|
||||||
|
|
||||||
FSpecView := TSpectrumView.Create;
|
FSpecView := TSpectrumView.Create;
|
||||||
|
FSpecView.VfoOverlay := FVfoOverlay;
|
||||||
FSpecView.VfoA := FVfoA;
|
FSpecView.VfoA := FVfoA;
|
||||||
FSpecView.VfoB := FVfoB;
|
FSpecView.VfoB := FVfoB;
|
||||||
FSpecView.ActiveVfo := FActiveVfo;
|
FSpecView.ActiveVfo := FActiveVfo;
|
||||||
@@ -1387,6 +1391,7 @@ begin
|
|||||||
// Audio output/input — создаём объекты сейчас, открываем после показа формы
|
// Audio output/input — создаём объекты сейчас, открываем после показа формы
|
||||||
// (Pa_Initialize на Linux пишет в stderr до перехвата сигналов FPC)
|
// (Pa_Initialize на Linux пишет в stderr до перехвата сигналов FPC)
|
||||||
FAudioOut := TAudioOutput.Create(48000);
|
FAudioOut := TAudioOutput.Create(48000);
|
||||||
|
FAudioOut.OutputBufferSize := FSettings.LoadAudioBufferSize;
|
||||||
FAudioIn := TAudioInput.Create(48000);
|
FAudioIn := TAudioInput.Create(48000);
|
||||||
|
|
||||||
FMeterTimer := TTimer.Create(Self);
|
FMeterTimer := TTimer.Create(Self);
|
||||||
@@ -4417,21 +4422,16 @@ begin
|
|||||||
FNetwork.SendFullHP;
|
FNetwork.SendFullHP;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Перерисовываем спектр всегда (маркер и полоса двигаются)
|
// Не рисуем синхронно из wheel/drag path: частые события мыши иначе
|
||||||
// Если идёт drag — не вызываем Draw напрямую, таймер подхватит
|
// забивают UI-поток и мешают аудио. Таймер подхватит ближайший кадр.
|
||||||
SyncSpecViewFreq;
|
SyncSpecViewFreq;
|
||||||
if FSpecDrag then
|
FSpectrumDirty := True;
|
||||||
FSpectrumDirty := True
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
FSpecView.DrawSpectrum;
|
|
||||||
PbSpectrum.Invalidate;
|
PbSpectrum.Invalidate;
|
||||||
if Scrolled then
|
if Scrolled then
|
||||||
begin
|
begin
|
||||||
FSpecView.DrawWaterfall;
|
FWaterfallDirty := True;
|
||||||
PbWaterfall.Invalidate;
|
PbWaterfall.Invalidate;
|
||||||
end;
|
end;
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -4648,6 +4648,8 @@ end;
|
|||||||
procedure TMainForm.PbSpectrumMouseDown(Sender: TObject; Button: TMouseButton;
|
procedure TMainForm.PbSpectrumMouseDown(Sender: TObject; Button: TMouseButton;
|
||||||
Shift: TShiftState; X, Y: Integer);
|
Shift: TShiftState; X, Y: Integer);
|
||||||
begin
|
begin
|
||||||
|
if Assigned(FVfoOverlay) and
|
||||||
|
FVfoOverlay.HandleMouseDown(Button, X, Y) then Exit;
|
||||||
if Assigned(FSampleRateOverlay) and
|
if Assigned(FSampleRateOverlay) and
|
||||||
FSampleRateOverlay.HandleMouseDown(Button, X, Y) then Exit;
|
FSampleRateOverlay.HandleMouseDown(Button, X, Y) then Exit;
|
||||||
|
|
||||||
@@ -4694,9 +4696,15 @@ begin
|
|||||||
if Assigned(FSampleRateOverlay) then
|
if Assigned(FSampleRateOverlay) then
|
||||||
begin
|
begin
|
||||||
FSampleRateOverlay.HandleMouseMove(X, Y);
|
FSampleRateOverlay.HandleMouseMove(X, Y);
|
||||||
if (FSampleRateOverlay.HotIdx >= 0) and (not FSpecDrag) then
|
end;
|
||||||
|
if Assigned(FVfoOverlay) then
|
||||||
|
FVfoOverlay.HandleMouseMove(X, Y);
|
||||||
|
if not FSpecDrag then
|
||||||
|
begin
|
||||||
|
if (Assigned(FVfoOverlay) and (FVfoOverlay.HotIdx >= 0)) or
|
||||||
|
(Assigned(FSampleRateOverlay) and (FSampleRateOverlay.HotIdx >= 0)) then
|
||||||
PbSpectrum.Cursor := crHandPoint
|
PbSpectrum.Cursor := crHandPoint
|
||||||
else if not FSpecDrag then
|
else
|
||||||
PbSpectrum.Cursor := crDefault;
|
PbSpectrum.Cursor := crDefault;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -4726,6 +4734,8 @@ procedure TMainForm.PbSpectrumMouseLeave(Sender: TObject);
|
|||||||
begin
|
begin
|
||||||
if Assigned(FSampleRateOverlay) then
|
if Assigned(FSampleRateOverlay) then
|
||||||
FSampleRateOverlay.HandleMouseLeave;
|
FSampleRateOverlay.HandleMouseLeave;
|
||||||
|
if Assigned(FVfoOverlay) then
|
||||||
|
FVfoOverlay.HandleMouseLeave;
|
||||||
if not FSpecDrag then PbSpectrum.Cursor := crDefault;
|
if not FSpecDrag then PbSpectrum.Cursor := crDefault;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -5304,18 +5314,17 @@ begin
|
|||||||
if FPanelHidden then
|
if FPanelHidden then
|
||||||
begin
|
begin
|
||||||
PanelLeft.Width := 0;
|
PanelLeft.Width := 0;
|
||||||
FVfoOverlay.Parent := PanelRight;
|
|
||||||
FVfoOverlay.Width := 260;
|
FVfoOverlay.Width := 260;
|
||||||
FVfoOverlay.Height := 136; // OVL_H_NORM — расширяется сам при открытии AGC-пикера
|
FVfoOverlay.Height := 136; // OVL_H_NORM — расширяется сам при открытии AGC-пикера
|
||||||
FVfoOverlay.SetState(FMode, FFilterBW, FVfoA, FLastSMeter);
|
FVfoOverlay.SetState(FMode, FFilterBW, FVfoA, FLastSMeter);
|
||||||
FVfoOverlay.SetDSPState(BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0, FAGCMode);
|
FVfoOverlay.SetDSPState(BtnNR.Tag, BtnNB.Tag, BtnSNB.Tag <> 0, BtnANF.Tag <> 0, FAGCMode);
|
||||||
FVfoOverlay.Visible := True;
|
FVfoOverlay.Visible := True;
|
||||||
FVfoOverlay.BringToFront;
|
|
||||||
PositionVfoOverlay;
|
PositionVfoOverlay;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
PanelLeft.Width := 232;
|
PanelLeft.Width := 232;
|
||||||
|
FVfoOverlay.HandleMouseLeave;
|
||||||
FVfoOverlay.Visible := False;
|
FVfoOverlay.Visible := False;
|
||||||
end;
|
end;
|
||||||
if Assigned(FSampleRateOverlay) then
|
if Assigned(FSampleRateOverlay) then
|
||||||
@@ -5326,20 +5335,40 @@ end;
|
|||||||
|
|
||||||
procedure TMainForm.PositionVfoOverlay;
|
procedure TMainForm.PositionVfoOverlay;
|
||||||
var
|
var
|
||||||
VfoX, OW, SpTop: Integer;
|
VfoX, FilterEndX, OW, NewLeft, NewTop: Integer;
|
||||||
|
HiHz: Double;
|
||||||
begin
|
begin
|
||||||
if not Assigned(FVfoOverlay) or not FVfoOverlay.Visible then Exit;
|
if not Assigned(FVfoOverlay) or not FVfoOverlay.Visible then Exit;
|
||||||
OW := FVfoOverlay.Width;
|
OW := FVfoOverlay.Width;
|
||||||
SpTop := PbSpectrum.Top; // смещение PbSpectrum внутри PanelRight
|
|
||||||
|
|
||||||
if PbSpectrum.Width > 0 then
|
if (PbSpectrum.Width > 0) and (FSpanHz > 0) then
|
||||||
|
begin
|
||||||
VfoX := Round((FVfoA - FCenterFreq + FSpanHz / 2) / FSpanHz * PbSpectrum.Width)
|
VfoX := Round((FVfoA - FCenterFreq + FSpanHz / 2) / FSpanHz * PbSpectrum.Width)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
VfoX := PbSpectrum.Width div 2;
|
VfoX := PbSpectrum.Width div 2;
|
||||||
|
|
||||||
// Всегда справа от VFO-линии
|
case FMode of
|
||||||
FVfoOverlay.Left := Min(PanelRight.Width - OW - 4, VfoX + 6);
|
MODE_LSB: HiHz := -100;
|
||||||
FVfoOverlay.Top := SpTop + 6;
|
MODE_USB: HiHz := FFilterBW;
|
||||||
|
else
|
||||||
|
HiHz := FFilterBW / 2;
|
||||||
|
end;
|
||||||
|
if (PbSpectrum.Width > 0) and (FSpanHz > 0) then
|
||||||
|
FilterEndX := VfoX + Round(HiHz / FSpanHz * PbSpectrum.Width)
|
||||||
|
else
|
||||||
|
FilterEndX := VfoX;
|
||||||
|
FilterEndX := Max(0, Min(PbSpectrum.Width - 1, FilterEndX));
|
||||||
|
|
||||||
|
// Всегда справа от правого края полосы фильтра.
|
||||||
|
NewLeft := Max(4, Min(PbSpectrum.Width - OW - 4, FilterEndX + 6));
|
||||||
|
NewTop := 16;
|
||||||
|
if (FVfoOverlay.Left <> NewLeft) or (FVfoOverlay.Top <> NewTop) then
|
||||||
|
begin
|
||||||
|
FVfoOverlay.Left := NewLeft;
|
||||||
|
FVfoOverlay.Top := NewTop;
|
||||||
|
OnVfoOverlayInvalidate(FVfoOverlay);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.PbSpectrumDblClick(Sender: TObject);
|
procedure TMainForm.PbSpectrumDblClick(Sender: TObject);
|
||||||
@@ -5400,6 +5429,13 @@ begin
|
|||||||
FBandCache[FCurrentBand].AGCMode := FAGCMode;
|
FBandCache[FCurrentBand].AGCMode := FAGCMode;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.OnVfoOverlayInvalidate(Sender: TObject);
|
||||||
|
begin
|
||||||
|
FSpectrumDirty := True;
|
||||||
|
if Assigned(FSpecView) then FSpecView.SpectrumDirty := True;
|
||||||
|
if Assigned(PbSpectrum) then PbSpectrum.Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TMainForm.OnSampleRateSelect(SampleRate: Integer);
|
procedure TMainForm.OnSampleRateSelect(SampleRate: Integer);
|
||||||
var
|
var
|
||||||
NewRate: Integer;
|
NewRate: Integer;
|
||||||
@@ -6731,6 +6767,29 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.ApplyAudioBufferSize(BufferSize: Integer);
|
||||||
|
var
|
||||||
|
WasOpen: Boolean;
|
||||||
|
begin
|
||||||
|
if BufferSize <= 128 then BufferSize := 128
|
||||||
|
else if BufferSize <= 256 then BufferSize := 256
|
||||||
|
else BufferSize := 512;
|
||||||
|
if FAudioOut.OutputBufferSize = BufferSize then Exit;
|
||||||
|
|
||||||
|
WasOpen := FAudioOut.IsOpen;
|
||||||
|
if WasOpen then FAudioOut.Close;
|
||||||
|
FAudioOut.OutputBufferSize := BufferSize;
|
||||||
|
if WasOpen then
|
||||||
|
begin
|
||||||
|
try
|
||||||
|
FAudioOut.Open;
|
||||||
|
except
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
FSettings.SaveAudioBufferSize(BufferSize);
|
||||||
|
FSettings.Save;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TMainForm.ApplyVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
procedure TMainForm.ApplyVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
||||||
begin
|
begin
|
||||||
FShowSpectrum := ShowSpectrum;
|
FShowSpectrum := ShowSpectrum;
|
||||||
@@ -6781,6 +6840,7 @@ begin
|
|||||||
SF.OnGridChange := ApplyGridParams;
|
SF.OnGridChange := ApplyGridParams;
|
||||||
SF.OnAudioDevChange := ApplyAudioDevice;
|
SF.OnAudioDevChange := ApplyAudioDevice;
|
||||||
SF.OnAudioInDevChange := ApplyAudioInputDevice;
|
SF.OnAudioInDevChange := ApplyAudioInputDevice;
|
||||||
|
SF.OnAudioBufferChange := ApplyAudioBufferSize;
|
||||||
SF.OnVisibilityChange := ApplyVisibility;
|
SF.OnVisibilityChange := ApplyVisibility;
|
||||||
SF.OnFPSChange := ApplyFPS;
|
SF.OnFPSChange := ApplyFPS;
|
||||||
SF.OnPAChange := OnPASettingsChange;
|
SF.OnPAChange := OnPASettingsChange;
|
||||||
@@ -6821,6 +6881,7 @@ begin
|
|||||||
SF.LoadFPS(FDisplayFPS);
|
SF.LoadFPS(FDisplayFPS);
|
||||||
SF.LoadLightTheme(FLightTheme);
|
SF.LoadLightTheme(FLightTheme);
|
||||||
SF.LoadFreqMhzDigits(FFreqMhzDigits);
|
SF.LoadFreqMhzDigits(FFreqMhzDigits);
|
||||||
|
SF.LoadAudioBufferSize(FAudioOut.OutputBufferSize);
|
||||||
// Загружаем текущие значения
|
// Загружаем текущие значения
|
||||||
if FWDSPReady then
|
if FWDSPReady then
|
||||||
SF.LoadValues(
|
SF.LoadValues(
|
||||||
|
|||||||
@@ -286,6 +286,8 @@ type
|
|||||||
procedure LoadWindowBounds(out L, T, W, H: Integer);
|
procedure LoadWindowBounds(out L, T, W, H: Integer);
|
||||||
procedure SaveStartupPreview(VfoA, VfoB: Double; SampleRate: Integer);
|
procedure SaveStartupPreview(VfoA, VfoB: Double; SampleRate: Integer);
|
||||||
function LoadStartupPreview(out VfoA, VfoB: Double; out SampleRate: Integer): Boolean;
|
function LoadStartupPreview(out VfoA, VfoB: Double; out SampleRate: Integer): Boolean;
|
||||||
|
procedure SaveAudioBufferSize(BufferSize: Integer);
|
||||||
|
function LoadAudioBufferSize: Integer;
|
||||||
// CAT settings — не привязаны к MAC, хранятся в корне JSON (доступны до подключения)
|
// CAT settings — не привязаны к MAC, хранятся в корне JSON (доступны до подключения)
|
||||||
procedure SaveCATSettings(const G: TGlobalSettings);
|
procedure SaveCATSettings(const G: TGlobalSettings);
|
||||||
procedure LoadCATSettings(var G: TGlobalSettings);
|
procedure LoadCATSettings(var G: TGlobalSettings);
|
||||||
@@ -798,6 +800,27 @@ begin
|
|||||||
SampleRate := JI(O, 'sample_rate', 192000);
|
SampleRate := JI(O, 'sample_rate', 192000);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TSettingsManager.SaveAudioBufferSize(BufferSize: Integer);
|
||||||
|
var O: TJSONObject;
|
||||||
|
begin
|
||||||
|
if BufferSize <= 128 then BufferSize := 128
|
||||||
|
else if BufferSize <= 256 then BufferSize := 256
|
||||||
|
else BufferSize := 512;
|
||||||
|
O := EnsureObj(FRoot, 'audio');
|
||||||
|
JW(O, 'output_buffer_size', BufferSize);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSettingsManager.LoadAudioBufferSize: Integer;
|
||||||
|
var O: TJSONObject;
|
||||||
|
begin
|
||||||
|
if FRoot.Find('audio') = nil then Exit(128);
|
||||||
|
O := EnsureObj(FRoot, 'audio');
|
||||||
|
Result := JI(O, 'output_buffer_size', 128);
|
||||||
|
if Result <= 128 then Result := 128
|
||||||
|
else if Result <= 256 then Result := 256
|
||||||
|
else Result := 512;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TSettingsManager.SaveCATSettings(const G: TGlobalSettings);
|
procedure TSettingsManager.SaveCATSettings(const G: TGlobalSettings);
|
||||||
var O: TJSONObject; i: Integer;
|
var O: TJSONObject; i: Integer;
|
||||||
begin
|
begin
|
||||||
|
|||||||
+48
-1
@@ -46,6 +46,7 @@ type
|
|||||||
TOnGridParamChange = procedure(RefLevel, Range, GridStep: Double) of object;
|
TOnGridParamChange = procedure(RefLevel, Range, GridStep: Double) of object;
|
||||||
TOnAudioDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
|
TOnAudioDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
|
||||||
TOnAudioInDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
|
TOnAudioInDevChange = procedure(DevIndex: Integer; const DevName: string) of object;
|
||||||
|
TOnAudioBufferChange = procedure(BufferSize: Integer) of object;
|
||||||
TOnVisibilityChange = procedure(ShowSpectrum, ShowWaterfall: Boolean) of object;
|
TOnVisibilityChange = procedure(ShowSpectrum, ShowWaterfall: Boolean) of object;
|
||||||
TOnFPSChange = procedure(FPS: Integer) of object;
|
TOnFPSChange = procedure(FPS: Integer) of object;
|
||||||
TOnThemeChange = procedure(LightTheme: Boolean) of object;
|
TOnThemeChange = procedure(LightTheme: Boolean) of object;
|
||||||
@@ -94,6 +95,7 @@ type
|
|||||||
FCmbTXDev: TComboBox;
|
FCmbTXDev: TComboBox;
|
||||||
FTXDevIndices: array[0..63] of Integer;
|
FTXDevIndices: array[0..63] of Integer;
|
||||||
FTXDevCount: Integer;
|
FTXDevCount: Integer;
|
||||||
|
FCmbAudioBuffer: TComboBox;
|
||||||
|
|
||||||
// ---- RX1 sub-tab controls ----
|
// ---- RX1 sub-tab controls ----
|
||||||
FCmbFFTSize: TComboBox;
|
FCmbFFTSize: TComboBox;
|
||||||
@@ -263,6 +265,7 @@ type
|
|||||||
FOnGridChange: TOnGridParamChange;
|
FOnGridChange: TOnGridParamChange;
|
||||||
FOnAudioDevChange: TOnAudioDevChange;
|
FOnAudioDevChange: TOnAudioDevChange;
|
||||||
FOnAudioInDevChange: TOnAudioInDevChange;
|
FOnAudioInDevChange: TOnAudioInDevChange;
|
||||||
|
FOnAudioBufferChange: TOnAudioBufferChange;
|
||||||
FOnVisibilityChange: TOnVisibilityChange;
|
FOnVisibilityChange: TOnVisibilityChange;
|
||||||
FOnFPSChange: TOnFPSChange;
|
FOnFPSChange: TOnFPSChange;
|
||||||
FOnThemeChange: TOnThemeChange;
|
FOnThemeChange: TOnThemeChange;
|
||||||
@@ -325,6 +328,7 @@ type
|
|||||||
procedure OnGridStepChange(Sender: TObject);
|
procedure OnGridStepChange(Sender: TObject);
|
||||||
procedure OnRXDevChange(Sender: TObject);
|
procedure OnRXDevChange(Sender: TObject);
|
||||||
procedure OnTXDevChange(Sender: TObject);
|
procedure OnTXDevChange(Sender: TObject);
|
||||||
|
procedure OnAudioBufferCmbChange(Sender: TObject);
|
||||||
procedure OnVisibilityChkChange(Sender: TObject);
|
procedure OnVisibilityChkChange(Sender: TObject);
|
||||||
procedure OnFPSCmbChange(Sender: TObject);
|
procedure OnFPSCmbChange(Sender: TObject);
|
||||||
procedure OnLightThemeChkChange(Sender: TObject);
|
procedure OnLightThemeChkChange(Sender: TObject);
|
||||||
@@ -364,6 +368,7 @@ type
|
|||||||
const AudioInDevName: string);
|
const AudioInDevName: string);
|
||||||
|
|
||||||
procedure RefreshAudioDevices(AudioOut: TAudioOutput; AudioIn: TAudioInput);
|
procedure RefreshAudioDevices(AudioOut: TAudioOutput; AudioIn: TAudioInput);
|
||||||
|
procedure LoadAudioBufferSize(BufferSize: Integer);
|
||||||
procedure LoadVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
procedure LoadVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
||||||
procedure LoadFPS(FPS: Integer);
|
procedure LoadFPS(FPS: Integer);
|
||||||
procedure LoadLightTheme(ALight: Boolean);
|
procedure LoadLightTheme(ALight: Boolean);
|
||||||
@@ -393,6 +398,7 @@ type
|
|||||||
property OnGridChange: TOnGridParamChange read FOnGridChange write FOnGridChange;
|
property OnGridChange: TOnGridParamChange read FOnGridChange write FOnGridChange;
|
||||||
property OnAudioDevChange: TOnAudioDevChange read FOnAudioDevChange write FOnAudioDevChange;
|
property OnAudioDevChange: TOnAudioDevChange read FOnAudioDevChange write FOnAudioDevChange;
|
||||||
property OnAudioInDevChange: TOnAudioInDevChange read FOnAudioInDevChange write FOnAudioInDevChange;
|
property OnAudioInDevChange: TOnAudioInDevChange read FOnAudioInDevChange write FOnAudioInDevChange;
|
||||||
|
property OnAudioBufferChange: TOnAudioBufferChange read FOnAudioBufferChange write FOnAudioBufferChange;
|
||||||
property OnVisibilityChange: TOnVisibilityChange read FOnVisibilityChange write FOnVisibilityChange;
|
property OnVisibilityChange: TOnVisibilityChange read FOnVisibilityChange write FOnVisibilityChange;
|
||||||
property OnFPSChange: TOnFPSChange read FOnFPSChange write FOnFPSChange;
|
property OnFPSChange: TOnFPSChange read FOnFPSChange write FOnFPSChange;
|
||||||
property OnThemeChange: TOnThemeChange read FOnThemeChange write FOnThemeChange;
|
property OnThemeChange: TOnThemeChange read FOnThemeChange write FOnThemeChange;
|
||||||
@@ -737,7 +743,7 @@ begin
|
|||||||
Font.Color := CLR_TEXTDIM;
|
Font.Color := CLR_TEXTDIM;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Grp := MakeGroupPanel(FPageAudio, 'Audio Devices', MARGIN, 86, 650, 142);
|
Grp := MakeGroupPanel(FPageAudio, 'Audio Devices', MARGIN, 86, 650, 184);
|
||||||
|
|
||||||
MakeLbl(Grp, 'TX input device', PAD, R1 + 5, LW);
|
MakeLbl(Grp, 'TX input device', PAD, R1 + 5, LW);
|
||||||
FCmbTXDev := MakeCombo(Grp, CX, R1, CMB_W, OnTXDevChange);
|
FCmbTXDev := MakeCombo(Grp, CX, R1, CMB_W, OnTXDevChange);
|
||||||
@@ -746,6 +752,13 @@ begin
|
|||||||
MakeLbl(Grp, 'RX output device', PAD, R1 + STEP + 5, LW);
|
MakeLbl(Grp, 'RX output device', PAD, R1 + STEP + 5, LW);
|
||||||
FCmbRXDev := MakeCombo(Grp, CX, R1 + STEP, CMB_W, OnRXDevChange);
|
FCmbRXDev := MakeCombo(Grp, CX, R1 + STEP, CMB_W, OnRXDevChange);
|
||||||
FCmbRXDev.Items.Add('Default');
|
FCmbRXDev.Items.Add('Default');
|
||||||
|
|
||||||
|
MakeLbl(Grp, 'Output buffer', PAD, R1 + STEP * 2 + 5, LW);
|
||||||
|
FCmbAudioBuffer := MakeCombo(Grp, CX, R1 + STEP * 2, 160, OnAudioBufferCmbChange);
|
||||||
|
FCmbAudioBuffer.Items.Add('128 frames');
|
||||||
|
FCmbAudioBuffer.Items.Add('256 frames');
|
||||||
|
FCmbAudioBuffer.Items.Add('512 frames');
|
||||||
|
FCmbAudioBuffer.ItemIndex := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -1972,6 +1985,25 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// LoadAudioBufferSize
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
procedure TSettingsForm.LoadAudioBufferSize(BufferSize: Integer);
|
||||||
|
begin
|
||||||
|
FLoading := True;
|
||||||
|
try
|
||||||
|
case BufferSize of
|
||||||
|
256: FCmbAudioBuffer.ItemIndex := 1;
|
||||||
|
512: FCmbAudioBuffer.ItemIndex := 2;
|
||||||
|
else
|
||||||
|
FCmbAudioBuffer.ItemIndex := 0;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
FLoading := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// RefreshAudioDevices
|
// RefreshAudioDevices
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -2243,6 +2275,21 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TSettingsForm.OnAudioBufferCmbChange(Sender: TObject);
|
||||||
|
var
|
||||||
|
BufferSize: Integer;
|
||||||
|
begin
|
||||||
|
if FLoading then Exit;
|
||||||
|
if not Assigned(FOnAudioBufferChange) then Exit;
|
||||||
|
case FCmbAudioBuffer.ItemIndex of
|
||||||
|
1: BufferSize := 256;
|
||||||
|
2: BufferSize := 512;
|
||||||
|
else
|
||||||
|
BufferSize := 128;
|
||||||
|
end;
|
||||||
|
FOnAudioBufferChange(BufferSize);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TSettingsForm.LoadVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
procedure TSettingsForm.LoadVisibility(ShowSpectrum, ShowWaterfall: Boolean);
|
||||||
begin
|
begin
|
||||||
FLoading := True;
|
FLoading := True;
|
||||||
|
|||||||
+5
-1
@@ -20,7 +20,7 @@ interface
|
|||||||
uses
|
uses
|
||||||
Classes, SysUtils, Graphics, ExtCtrls, Controls, Math,
|
Classes, SysUtils, Graphics, ExtCtrls, Controls, Math,
|
||||||
IntfGraphics, FPImage, LCLIntf, LCLType, GraphType, AppTheme,
|
IntfGraphics, FPImage, LCLIntf, LCLType, GraphType, AppTheme,
|
||||||
AlertOverlay, SampleRateOverlay,
|
AlertOverlay, SampleRateOverlay, VfoOverlay,
|
||||||
WaterfallView, SMeterView, RulerView;
|
WaterfallView, SMeterView, RulerView;
|
||||||
|
|
||||||
const
|
const
|
||||||
@@ -70,6 +70,7 @@ type
|
|||||||
// ── ADC overload overlay ─────────────────────────────────────────────────
|
// ── ADC overload overlay ─────────────────────────────────────────────────
|
||||||
FADCOverloadVisible: Boolean;
|
FADCOverloadVisible: Boolean;
|
||||||
FSampleRateOverlay: TSampleRateOverlay;
|
FSampleRateOverlay: TSampleRateOverlay;
|
||||||
|
FVfoOverlay: TVfoOverlay;
|
||||||
// ── Marker ────────────────────────────────────────────────────────────────
|
// ── Marker ────────────────────────────────────────────────────────────────
|
||||||
FMarkerActive: Boolean;
|
FMarkerActive: Boolean;
|
||||||
FMarkerX: Integer;
|
FMarkerX: Integer;
|
||||||
@@ -199,6 +200,7 @@ type
|
|||||||
property PbRuler: TPaintBox write SetPbRuler;
|
property PbRuler: TPaintBox write SetPbRuler;
|
||||||
property PbSMeterRight: TPaintBox write SetPbSMeterRight;
|
property PbSMeterRight: TPaintBox write SetPbSMeterRight;
|
||||||
property SampleRateOverlay: TSampleRateOverlay read FSampleRateOverlay write FSampleRateOverlay;
|
property SampleRateOverlay: TSampleRateOverlay read FSampleRateOverlay write FSampleRateOverlay;
|
||||||
|
property VfoOverlay: TVfoOverlay read FVfoOverlay write FVfoOverlay;
|
||||||
|
|
||||||
// ── Данные от DSP ─────────────────────────────────────────────────────────
|
// ── Данные от DSP ─────────────────────────────────────────────────────────
|
||||||
procedure SetSpectrumData(const Pixels: array of Single; Count: Integer);
|
procedure SetSpectrumData(const Pixels: array of Single; Count: Integer);
|
||||||
@@ -918,6 +920,8 @@ begin
|
|||||||
DrawADCOverloadOverlay(C, W, H);
|
DrawADCOverloadOverlay(C, W, H);
|
||||||
if Assigned(FSampleRateOverlay) then
|
if Assigned(FSampleRateOverlay) then
|
||||||
FSampleRateOverlay.DrawOverlay(FSpectrumBitmap, C, W, H);
|
FSampleRateOverlay.DrawOverlay(FSpectrumBitmap, C, W, H);
|
||||||
|
if Assigned(FVfoOverlay) then
|
||||||
|
FVfoOverlay.DrawOverlay(FSpectrumBitmap, W, H);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// ────────────────────────────────────────────────────────────────────────────
|
// ────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
+153
-11
@@ -21,6 +21,7 @@ type
|
|||||||
FOnSelect: TModeFilterEvent;
|
FOnSelect: TModeFilterEvent;
|
||||||
FOnDSPChange: TDSPChangeEvent;
|
FOnDSPChange: TDSPChangeEvent;
|
||||||
FOnAGCChange: TAGCChangeEvent;
|
FOnAGCChange: TAGCChangeEvent;
|
||||||
|
FOnInvalidate: TNotifyEvent;
|
||||||
FModeFilter: array[0..7] of Integer;
|
FModeFilter: array[0..7] of Integer;
|
||||||
|
|
||||||
FNRMode: Integer; // 0=off, 1..4=NR/NR2/NR3/NR4
|
FNRMode: Integer; // 0=off, 1..4=NR/NR2/NR3/NR4
|
||||||
@@ -34,6 +35,8 @@ type
|
|||||||
FHitActions: array[0..24] of Integer;
|
FHitActions: array[0..24] of Integer;
|
||||||
FHitCount: Integer;
|
FHitCount: Integer;
|
||||||
FHotIdx: Integer;
|
FHotIdx: Integer;
|
||||||
|
FCacheBitmap: TBitmap;
|
||||||
|
FCacheDirty: Boolean;
|
||||||
|
|
||||||
procedure DrawSelf(C: TCanvas; W, H: Integer);
|
procedure DrawSelf(C: TCanvas; W, H: Integer);
|
||||||
procedure DrawSMeterBar(C: TCanvas; X, Y, BW, BH: Integer);
|
procedure DrawSMeterBar(C: TCanvas; X, Y, BW, BH: Integer);
|
||||||
@@ -47,6 +50,7 @@ type
|
|||||||
function GetFilterBWForMode(ModeIdx, FilterIdx: Integer): Integer;
|
function GetFilterBWForMode(ModeIdx, FilterIdx: Integer): Integer;
|
||||||
function GetFilterLblForMode(ModeIdx, FilterIdx: Integer): string;
|
function GetFilterLblForMode(ModeIdx, FilterIdx: Integer): string;
|
||||||
function GetFilterCountForMode(ModeIdx: Integer): Integer;
|
function GetFilterCountForMode(ModeIdx: Integer): Integer;
|
||||||
|
procedure RequestInvalidate;
|
||||||
|
|
||||||
protected
|
protected
|
||||||
procedure Paint; override;
|
procedure Paint; override;
|
||||||
@@ -57,19 +61,28 @@ type
|
|||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TComponent); override;
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure DrawOverlay(Target: TBitmap; W, H: Integer);
|
||||||
|
function HandleMouseDown(Button: TMouseButton; X, Y: Integer): Boolean;
|
||||||
|
function HandleMouseMove(X, Y: Integer): Boolean;
|
||||||
|
function HandleMouseLeave: Boolean;
|
||||||
procedure SetState(AMode, ABW: Integer; AVfoHz, ASMeterDB: Double);
|
procedure SetState(AMode, ABW: Integer; AVfoHz, ASMeterDB: Double);
|
||||||
procedure SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean;
|
procedure SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean;
|
||||||
AAGCMode: Integer);
|
AAGCMode: Integer);
|
||||||
procedure UpdateSMeter(DB: Double);
|
procedure UpdateSMeter(DB: Double);
|
||||||
procedure UpdateVfo(AVfoHz: Double);
|
procedure UpdateVfo(AVfoHz: Double);
|
||||||
|
property HotIdx: Integer read FHotIdx;
|
||||||
property OnSelect: TModeFilterEvent read FOnSelect write FOnSelect;
|
property OnSelect: TModeFilterEvent read FOnSelect write FOnSelect;
|
||||||
property OnDSPChange: TDSPChangeEvent read FOnDSPChange write FOnDSPChange;
|
property OnDSPChange: TDSPChangeEvent read FOnDSPChange write FOnDSPChange;
|
||||||
property OnAGCChange: TAGCChangeEvent read FOnAGCChange write FOnAGCChange;
|
property OnAGCChange: TAGCChangeEvent read FOnAGCChange write FOnAGCChange;
|
||||||
|
property OnInvalidate: TNotifyEvent read FOnInvalidate write FOnInvalidate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
const
|
const
|
||||||
|
KEY_COLOR = TColor($00FF00FF);
|
||||||
|
|
||||||
HIT_NR = -9;
|
HIT_NR = -9;
|
||||||
HIT_NB = -10;
|
HIT_NB = -10;
|
||||||
HIT_SNB = -11;
|
HIT_SNB = -11;
|
||||||
@@ -126,6 +139,53 @@ const
|
|||||||
// S-шкала: S1..S9
|
// S-шкала: S1..S9
|
||||||
DB_S: array[1..9] of Double = (-121,-115,-109,-103,-97,-91,-85,-79,-73);
|
DB_S: array[1..9] of Double = (-121,-115,-109,-103,-97,-91,-85,-79,-73);
|
||||||
|
|
||||||
|
procedure BlendBitmapKey(Target, Source: TBitmap; DstX, DstY: Integer; Alpha: Byte);
|
||||||
|
var
|
||||||
|
Y, X, SX0, SY0, SX1, SY1, TY, InvA: Integer;
|
||||||
|
SrcRow, DstRow: PByte;
|
||||||
|
begin
|
||||||
|
if (Target = nil) or (Source = nil) or (Alpha = 0) then Exit;
|
||||||
|
SX0 := 0;
|
||||||
|
SY0 := 0;
|
||||||
|
SX1 := Source.Width;
|
||||||
|
SY1 := Source.Height;
|
||||||
|
if DstX < 0 then begin SX0 := -DstX; DstX := 0; end;
|
||||||
|
if DstY < 0 then begin SY0 := -DstY; DstY := 0; end;
|
||||||
|
if DstX + (SX1 - SX0) > Target.Width then SX1 := SX0 + Target.Width - DstX;
|
||||||
|
if DstY + (SY1 - SY0) > Target.Height then SY1 := SY0 + Target.Height - DstY;
|
||||||
|
if (SX1 <= SX0) or (SY1 <= SY0) then Exit;
|
||||||
|
|
||||||
|
InvA := 255 - Alpha;
|
||||||
|
Target.BeginUpdate(False);
|
||||||
|
Source.BeginUpdate(False);
|
||||||
|
try
|
||||||
|
for Y := SY0 to SY1 - 1 do
|
||||||
|
begin
|
||||||
|
TY := DstY + (Y - SY0);
|
||||||
|
SrcRow := PByte(Source.ScanLine[Y]);
|
||||||
|
DstRow := PByte(Target.ScanLine[TY]);
|
||||||
|
if (SrcRow = nil) or (DstRow = nil) then Continue;
|
||||||
|
Inc(SrcRow, SX0 * 4);
|
||||||
|
Inc(DstRow, DstX * 4);
|
||||||
|
for X := SX0 to SX1 - 1 do
|
||||||
|
begin
|
||||||
|
// KEY_COLOR is clFuchsia in pf32bit B,G,R order.
|
||||||
|
if not ((SrcRow[0] = $FF) and (SrcRow[1] = $00) and (SrcRow[2] = $FF)) then
|
||||||
|
begin
|
||||||
|
DstRow[0] := Byte((Alpha * SrcRow[0] + InvA * DstRow[0]) div 255);
|
||||||
|
DstRow[1] := Byte((Alpha * SrcRow[1] + InvA * DstRow[1]) div 255);
|
||||||
|
DstRow[2] := Byte((Alpha * SrcRow[2] + InvA * DstRow[2]) div 255);
|
||||||
|
end;
|
||||||
|
Inc(SrcRow, 4);
|
||||||
|
Inc(DstRow, 4);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
Source.EndUpdate(False);
|
||||||
|
Target.EndUpdate(False);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function TVfoOverlay.GetFilterBWForMode(ModeIdx, FilterIdx: Integer): Integer;
|
function TVfoOverlay.GetFilterBWForMode(ModeIdx, FilterIdx: Integer): Integer;
|
||||||
begin
|
begin
|
||||||
case ModeIdx of
|
case ModeIdx of
|
||||||
@@ -206,11 +266,20 @@ begin
|
|||||||
FANFOn := False;
|
FANFOn := False;
|
||||||
FAGCMode := 0;
|
FAGCMode := 0;
|
||||||
FAGCPickerOpen := False;
|
FAGCPickerOpen := False;
|
||||||
|
FCacheBitmap := TBitmap.Create;
|
||||||
|
FCacheBitmap.PixelFormat := pf32bit;
|
||||||
|
FCacheDirty := True;
|
||||||
Width := 260;
|
Width := 260;
|
||||||
Height := OVL_H_NORM;
|
Height := OVL_H_NORM;
|
||||||
Cursor := crHandPoint;
|
Cursor := crHandPoint;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
destructor TVfoOverlay.Destroy;
|
||||||
|
begin
|
||||||
|
FCacheBitmap.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TVfoOverlay.RegisterHit(HR: TRect; HV: Integer);
|
procedure TVfoOverlay.RegisterHit(HR: TRect; HV: Integer);
|
||||||
begin
|
begin
|
||||||
if FHitCount > High(FHitRects) then Exit;
|
if FHitCount > High(FHitRects) then Exit;
|
||||||
@@ -219,6 +288,13 @@ begin
|
|||||||
Inc(FHitCount);
|
Inc(FHitCount);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TVfoOverlay.RequestInvalidate;
|
||||||
|
begin
|
||||||
|
FCacheDirty := True;
|
||||||
|
inherited Invalidate;
|
||||||
|
if Assigned(FOnInvalidate) then FOnInvalidate(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
function TVfoOverlay.FormatFreq(Hz: Double): string;
|
function TVfoOverlay.FormatFreq(Hz: Double): string;
|
||||||
var
|
var
|
||||||
iM, iK, iH: Integer;
|
iM, iK, iH: Integer;
|
||||||
@@ -534,6 +610,72 @@ begin
|
|||||||
DrawSelf(Canvas, Width, Height);
|
DrawSelf(Canvas, Width, Height);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TVfoOverlay.DrawOverlay(Target: TBitmap; W, H: Integer);
|
||||||
|
begin
|
||||||
|
if (not Visible) or (Target = nil) or (Width <= 0) or (Height <= 0) then Exit;
|
||||||
|
if (Left >= W) or (Top >= H) then Exit;
|
||||||
|
|
||||||
|
if (FCacheBitmap.Width <> Width) or (FCacheBitmap.Height <> Height) then
|
||||||
|
begin
|
||||||
|
FCacheBitmap.SetSize(Width, Height);
|
||||||
|
FCacheDirty := True;
|
||||||
|
end;
|
||||||
|
if FCacheDirty then
|
||||||
|
begin
|
||||||
|
FCacheBitmap.Canvas.Brush.Color := KEY_COLOR;
|
||||||
|
FCacheBitmap.Canvas.Brush.Style := bsSolid;
|
||||||
|
FCacheBitmap.Canvas.Pen.Style := psClear;
|
||||||
|
FCacheBitmap.Canvas.FillRect(Rect(0, 0, Width, Height));
|
||||||
|
DrawSelf(FCacheBitmap.Canvas, Width, Height);
|
||||||
|
FCacheDirty := False;
|
||||||
|
end;
|
||||||
|
BlendBitmapKey(Target, FCacheBitmap, Left, Top, 218);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TVfoOverlay.HandleMouseDown(Button: TMouseButton; X, Y: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
if not Visible then Exit;
|
||||||
|
if (X < Left) or (Y < Top) or (X >= Left + Width) or (Y >= Top + Height) then Exit;
|
||||||
|
MouseDown(Button, [], X - Left, Y - Top);
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TVfoOverlay.HandleMouseMove(X, Y: Integer): Boolean;
|
||||||
|
var
|
||||||
|
I, OldHot, LX, LY: Integer;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
if not Visible then Exit;
|
||||||
|
LX := X - Left;
|
||||||
|
LY := Y - Top;
|
||||||
|
OldHot := FHotIdx;
|
||||||
|
FHotIdx := -1;
|
||||||
|
if (LX >= 0) and (LY >= 0) and (LX < Width) and (LY < Height) then
|
||||||
|
for I := 0 to FHitCount - 1 do
|
||||||
|
if PtInRect(FHitRects[I], Point(LX, LY)) then
|
||||||
|
begin
|
||||||
|
FHotIdx := I;
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
if FHotIdx <> OldHot then
|
||||||
|
begin
|
||||||
|
RequestInvalidate;
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TVfoOverlay.HandleMouseLeave: Boolean;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
if FHotIdx >= 0 then
|
||||||
|
begin
|
||||||
|
FHotIdx := -1;
|
||||||
|
RequestInvalidate;
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TVfoOverlay.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
procedure TVfoOverlay.MouseDown(Button: TMouseButton; Shift: TShiftState;
|
||||||
X, Y: Integer);
|
X, Y: Integer);
|
||||||
var
|
var
|
||||||
@@ -556,7 +698,7 @@ begin
|
|||||||
FModeFilter[FMode] := FFilterBW;
|
FModeFilter[FMode] := FFilterBW;
|
||||||
FMode := NewMode;
|
FMode := NewMode;
|
||||||
FFilterBW := FModeFilter[FMode];
|
FFilterBW := FModeFilter[FMode];
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
if Assigned(FOnSelect) then FOnSelect(FMode, FFilterBW);
|
if Assigned(FOnSelect) then FOnSelect(FMode, FFilterBW);
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
@@ -568,7 +710,7 @@ begin
|
|||||||
FAGCPickerOpen := not FAGCPickerOpen;
|
FAGCPickerOpen := not FAGCPickerOpen;
|
||||||
if FAGCPickerOpen then Height := OVL_H_AGC
|
if FAGCPickerOpen then Height := OVL_H_AGC
|
||||||
else Height := OVL_H_NORM;
|
else Height := OVL_H_NORM;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
@@ -578,7 +720,7 @@ begin
|
|||||||
HIT_SNB: FSNBOn := not FSNBOn;
|
HIT_SNB: FSNBOn := not FSNBOn;
|
||||||
HIT_ANF: FANFOn := not FANFOn;
|
HIT_ANF: FANFOn := not FANFOn;
|
||||||
end;
|
end;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
if Assigned(FOnDSPChange) then
|
if Assigned(FOnDSPChange) then
|
||||||
FOnDSPChange(FNRMode, FNBMode, FSNBOn, FANFOn);
|
FOnDSPChange(FNRMode, FNBMode, FSNBOn, FANFOn);
|
||||||
end;
|
end;
|
||||||
@@ -590,7 +732,7 @@ begin
|
|||||||
FAGCMode := HV - HIT_AGC_PICK_BASE;
|
FAGCMode := HV - HIT_AGC_PICK_BASE;
|
||||||
FAGCPickerOpen := False;
|
FAGCPickerOpen := False;
|
||||||
Height := OVL_H_NORM;
|
Height := OVL_H_NORM;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
if Assigned(FOnAGCChange) then FOnAGCChange(FAGCMode);
|
if Assigned(FOnAGCChange) then FOnAGCChange(FAGCMode);
|
||||||
end
|
end
|
||||||
else if HV > 0 then
|
else if HV > 0 then
|
||||||
@@ -600,7 +742,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
FFilterBW := NewBW;
|
FFilterBW := NewBW;
|
||||||
FModeFilter[FMode] := FFilterBW;
|
FModeFilter[FMode] := FFilterBW;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
if Assigned(FOnSelect) then FOnSelect(FMode, FFilterBW);
|
if Assigned(FOnSelect) then FOnSelect(FMode, FFilterBW);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@@ -617,13 +759,13 @@ begin
|
|||||||
for I := 0 to FHitCount - 1 do
|
for I := 0 to FHitCount - 1 do
|
||||||
if PtInRect(FHitRects[I], Point(X, Y)) then
|
if PtInRect(FHitRects[I], Point(X, Y)) then
|
||||||
begin FHotIdx := I; Break; end;
|
begin FHotIdx := I; Break; end;
|
||||||
if FHotIdx <> OldHot then Invalidate;
|
if FHotIdx <> OldHot then RequestInvalidate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TVfoOverlay.MouseLeave;
|
procedure TVfoOverlay.MouseLeave;
|
||||||
begin
|
begin
|
||||||
inherited;
|
inherited;
|
||||||
if FHotIdx >= 0 then begin FHotIdx := -1; Invalidate; end;
|
if FHotIdx >= 0 then begin FHotIdx := -1; RequestInvalidate; end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TVfoOverlay.SetState(AMode, ABW: Integer; AVfoHz, ASMeterDB: Double);
|
procedure TVfoOverlay.SetState(AMode, ABW: Integer; AVfoHz, ASMeterDB: Double);
|
||||||
@@ -634,7 +776,7 @@ begin
|
|||||||
FModeFilter[FMode] := ABW;
|
FModeFilter[FMode] := ABW;
|
||||||
FVfoHz := AVfoHz;
|
FVfoHz := AVfoHz;
|
||||||
FSMeterDB := ASMeterDB;
|
FSMeterDB := ASMeterDB;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TVfoOverlay.SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean;
|
procedure TVfoOverlay.SetDSPState(ANRMode, ANBMode: Integer; ASNBOn, AANFOn: Boolean;
|
||||||
@@ -645,7 +787,7 @@ begin
|
|||||||
FSNBOn := ASNBOn;
|
FSNBOn := ASNBOn;
|
||||||
FANFOn := AANFOn;
|
FANFOn := AANFOn;
|
||||||
FAGCMode := AAGCMode;
|
FAGCMode := AAGCMode;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TVfoOverlay.UpdateSMeter(DB: Double);
|
procedure TVfoOverlay.UpdateSMeter(DB: Double);
|
||||||
@@ -654,7 +796,7 @@ begin
|
|||||||
if Abs(DB - FSMeterDB) > 0.4 then
|
if Abs(DB - FSMeterDB) > 0.4 then
|
||||||
begin
|
begin
|
||||||
FSMeterDB := DB;
|
FSMeterDB := DB;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -663,7 +805,7 @@ begin
|
|||||||
if FVfoHz <> AVfoHz then
|
if FVfoHz <> AVfoHz then
|
||||||
begin
|
begin
|
||||||
FVfoHz := AVfoHz;
|
FVfoHz := AVfoHz;
|
||||||
Invalidate;
|
RequestInvalidate;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user