mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
start work tx
This commit is contained in:
+327
-55
@@ -103,6 +103,9 @@ const
|
||||
IQ_QUEUE_SIZE = 64; // кол-во слотов (степень двойки для AND-маски)
|
||||
IQ_PKT_MAXBYTES = 1428; // max байт IQ данных в пакете (238*6)
|
||||
|
||||
// TX mic ring buffer — должен быть степенью двойки
|
||||
TX_MIC_RING = 8192;
|
||||
|
||||
type
|
||||
// Пакет в очереди между сетевым и DSP потоком
|
||||
TIQQueueItem = record
|
||||
@@ -118,6 +121,11 @@ type
|
||||
Count: Integer) of object;
|
||||
TOnSpectrumReady = procedure(const Pixels: array of Single;
|
||||
Count: Integer) of object;
|
||||
// TX IQ callback — вызывается из TTXDSPThread при готовности блока
|
||||
// Buf: interleaved [I0,Q0,I1,Q1,...] doubles, Count — кол-во IQ пар
|
||||
TOnTXIQReady = procedure(const Buf: array of Double; Count: Integer) of object;
|
||||
TOnWaterfallReady = procedure(const Pixels: array of Single;
|
||||
Count: Integer) of object;
|
||||
|
||||
{ TWDSPEngine }
|
||||
TWDSPEngine = class
|
||||
@@ -140,8 +148,8 @@ type
|
||||
FRXAccQ: array of Double;
|
||||
FRXAccPos: Integer;
|
||||
|
||||
// Snapshot буфер для Spectrum0
|
||||
FSnapBuf: array of Double;
|
||||
// Копия входных IQ для Spectrum0 (до fexchange0, который данные in-place перезаписывает)
|
||||
FSpecBuf: array of Double;
|
||||
// Аудио выходные буферы — аллоцируем один раз
|
||||
FOutL: array of Single;
|
||||
FOutR: array of Single;
|
||||
@@ -152,14 +160,36 @@ type
|
||||
FQueueTail: Integer; // читает DSP поток
|
||||
FQueueSem: PRTLEvent; // сигнал: есть новые данные (RTLEvent)
|
||||
FDSPRunning: Boolean;
|
||||
// Double буфер для Spectrum0 (принимает PDouble, не PdINREAL)
|
||||
FSpecBuf: array of Double; // Double буфер для Spectrum0 (PDouble)
|
||||
|
||||
FSpectrumPixels: array[0..SPECTRUM_PIXELS - 1] of Single;
|
||||
FFlp: array[0..0] of Integer; // for SetAnalyzer flp parameter
|
||||
FSpectrumPixels: array[0..SPECTRUM_PIXELS - 1] of Single;
|
||||
FWaterfallPixels: array[0..SPECTRUM_PIXELS - 1] of Single;
|
||||
FFlp: array[0..0] of Integer; // for SetAnalyzer flp parameter
|
||||
|
||||
FOnAudio: TOnAudioReady;
|
||||
FOnSpectrum: TOnSpectrumReady;
|
||||
// Настройки анализатора
|
||||
FFFTSize: Integer;
|
||||
FWindowType: Integer;
|
||||
FSpecDetector: Integer;
|
||||
FSpecAvgMode: Integer;
|
||||
FSpecAvgCount: Integer;
|
||||
FSpecBackmult: Double;
|
||||
FWfDetector: Integer;
|
||||
FWfAvgMode: Integer;
|
||||
FWfAvgCount: Integer;
|
||||
FWfBackmult: Double;
|
||||
|
||||
FOnAudio: TOnAudioReady;
|
||||
FOnSpectrum: TOnSpectrumReady;
|
||||
FOnWaterfall: TOnWaterfallReady;
|
||||
FOnTXIQ: TOnTXIQReady;
|
||||
|
||||
// TX mic ring buffer (lock-free: один writer — receive thread,
|
||||
// один reader — TX DSP thread)
|
||||
FTXMicRing: array[0..TX_MIC_RING-1] of Double;
|
||||
FTXMicHead: Integer; // пишет receive thread
|
||||
FTXMicTail: Integer; // читает TX DSP thread
|
||||
FTXMicSem: PRTLEvent; // сигнал: есть новые mic сэмплы
|
||||
FTXThread: TThread;
|
||||
FTXActive: Boolean;
|
||||
|
||||
FMode: Integer;
|
||||
FSMeter: Double;
|
||||
@@ -187,11 +217,13 @@ type
|
||||
procedure PushIQItemToDSP(const Item: TIQQueueItem);
|
||||
procedure OpenAnalyzer;
|
||||
procedure CloseAnalyzer;
|
||||
procedure ApplyAnalyzerSettings;
|
||||
procedure ApplyNRState;
|
||||
procedure ApplyNBState;
|
||||
procedure ApplySNBState;
|
||||
procedure ApplyANFState;
|
||||
procedure ApplyNoiseFilterState;
|
||||
procedure ProcessTXBlock; // вызывается из TTXDSPThread
|
||||
|
||||
public
|
||||
constructor Create(SampleRate: Integer = 192000;
|
||||
@@ -236,6 +268,15 @@ type
|
||||
procedure SetMicGain(GainDB: Double);
|
||||
procedure SetTXRun(Run: Boolean);
|
||||
|
||||
// TX mic вход — вызывается из receive thread (thread-safe lock-free)
|
||||
// Src: 16-bit big-endian hardware mic samples, N — кол-во сэмплов
|
||||
procedure PushTXMicSamples16(const Src: array of SmallInt; N: Integer);
|
||||
|
||||
// Настройки анализатора (применяются немедленно если открыт)
|
||||
procedure SetFFTParams(FFTSize, WinType: Integer);
|
||||
procedure SetSpectrumDisplay(Detector, AvgMode, AvgCount: Integer; Backmult: Double);
|
||||
procedure SetWaterfallDisplay(Detector, AvgMode, AvgCount: Integer; Backmult: Double);
|
||||
|
||||
// Spectrum — вызывать из таймера (~20 fps)
|
||||
procedure UpdateSpectrum;
|
||||
procedure GetSpectrumData(var Pixels: array of Single; var Count: Integer);
|
||||
@@ -259,12 +300,69 @@ type
|
||||
property SNBEnabled: Boolean read FSNBEnabled;
|
||||
property LastError: string read FLastError;
|
||||
property SMeter: Double read FSMeter;
|
||||
property OnAudio: TOnAudioReady read FOnAudio write FOnAudio;
|
||||
property OnSpectrum: TOnSpectrumReady read FOnSpectrum write FOnSpectrum;
|
||||
property FFTSize: Integer read FFFTSize;
|
||||
property WindowType: Integer read FWindowType;
|
||||
property SpecDetector: Integer read FSpecDetector;
|
||||
property SpecAvgMode: Integer read FSpecAvgMode;
|
||||
property SpecAvgCount: Integer read FSpecAvgCount;
|
||||
property SpecBackmult: Double read FSpecBackmult;
|
||||
property WfDetector: Integer read FWfDetector;
|
||||
property WfAvgMode: Integer read FWfAvgMode;
|
||||
property WfAvgCount: Integer read FWfAvgCount;
|
||||
property WfBackmult: Double read FWfBackmult;
|
||||
property OnAudio: TOnAudioReady read FOnAudio write FOnAudio;
|
||||
property OnSpectrum: TOnSpectrumReady read FOnSpectrum write FOnSpectrum;
|
||||
property OnWaterfall: TOnWaterfallReady read FOnWaterfall write FOnWaterfall;
|
||||
property OnTXIQ: TOnTXIQReady read FOnTXIQ write FOnTXIQ;
|
||||
property TXActive: Boolean read FTXActive;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
// ===========================================================================
|
||||
// TX DSP поток — читает mic ring buffer, вызывает fexchange0(TXA_CHAN),
|
||||
// сигнализирует о готовых IQ данных через FOnTXIQ callback.
|
||||
// Аналог tx_thread в piHPSDR/transmitter.c
|
||||
// ===========================================================================
|
||||
type
|
||||
TTXDSPThread = class(TThread)
|
||||
private
|
||||
FEngine: TWDSPEngine;
|
||||
protected
|
||||
procedure Execute; override;
|
||||
public
|
||||
constructor Create(AEngine: TWDSPEngine);
|
||||
end;
|
||||
|
||||
constructor TTXDSPThread.Create(AEngine: TWDSPEngine);
|
||||
begin
|
||||
FEngine := AEngine;
|
||||
FreeOnTerminate := False;
|
||||
inherited Create(False);
|
||||
end;
|
||||
|
||||
procedure TTXDSPThread.Execute;
|
||||
var
|
||||
Avail: Integer;
|
||||
begin
|
||||
while not Terminated do
|
||||
begin
|
||||
RTLEventWaitFor(FEngine.FTXMicSem, 50);
|
||||
if Terminated then Break;
|
||||
if not FEngine.FTXActive then Continue;
|
||||
|
||||
// Обрабатываем все накопившиеся блоки
|
||||
repeat
|
||||
Avail := (FEngine.FTXMicHead - FEngine.FTXMicTail + TX_MIC_RING)
|
||||
and (TX_MIC_RING - 1);
|
||||
if Avail >= FEngine.FAudioBufSize then
|
||||
FEngine.ProcessTXBlock
|
||||
else
|
||||
Break;
|
||||
until Terminated;
|
||||
end;
|
||||
end;
|
||||
|
||||
// ===========================================================================
|
||||
// DSP поток — обрабатывает IQ пакеты из очереди
|
||||
// Сетевой поток только кладёт пакеты, этот поток занимается DSP
|
||||
@@ -367,6 +465,17 @@ begin
|
||||
FSNBEnabled := False;
|
||||
FANFEnabled := False;
|
||||
FRXAccPos := 0;
|
||||
// Analyzer defaults
|
||||
FFFTSize := 4096;
|
||||
FWindowType := 5; // Kaiser
|
||||
FSpecDetector := 0; // Peak
|
||||
FSpecAvgMode := 3; // Log Recursive
|
||||
FSpecAvgCount := 2;
|
||||
FSpecBackmult := 0.45;
|
||||
FWfDetector := 2; // Average
|
||||
FWfAvgMode := 3; // Log Recursive
|
||||
FWfAvgCount := 2;
|
||||
FWfBackmult := 0.45;
|
||||
|
||||
SetLength(FRXIn, FBufSize * 2); // in_size пар @ FSampleRate (4096*2)
|
||||
SetLength(FRXOut, FAudioBufSize * 2); // out_size пар @ FAudioRate (1024*2)
|
||||
@@ -374,10 +483,9 @@ begin
|
||||
SetLength(FTXOut, FBufSize * 2);
|
||||
SetLength(FRXAccI, FBufSize);
|
||||
SetLength(FRXAccQ, FBufSize);
|
||||
SetLength(FSnapBuf, FBufSize * 2);
|
||||
SetLength(FSpecBuf, FBufSize * 2);
|
||||
SetLength(FOutL, FAudioBufSize);
|
||||
SetLength(FOutR, FAudioBufSize);
|
||||
SetLength(FSpecBuf, FBufSize * 2);
|
||||
|
||||
// Очередь и DSP поток
|
||||
FQueueHead := 0;
|
||||
@@ -385,12 +493,22 @@ begin
|
||||
FDSPRunning := False;
|
||||
FQueueSem := RTLEventCreate;
|
||||
FDSPThread := nil;
|
||||
|
||||
// TX инициализация
|
||||
FTXMicHead := 0;
|
||||
FTXMicTail := 0;
|
||||
FTXActive := False;
|
||||
FTXThread := nil;
|
||||
FTXMicSem := RTLEventCreate;
|
||||
FillChar(FTXMicRing, SizeOf(FTXMicRing), 0);
|
||||
end;
|
||||
|
||||
destructor TWDSPEngine.Destroy;
|
||||
begin
|
||||
SetTXRun(False); // останавливаем TX поток если запущен
|
||||
Close;
|
||||
RTLEventDestroy(FQueueSem);
|
||||
RTLEventDestroy(FTXMicSem);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
@@ -400,10 +518,7 @@ end;
|
||||
|
||||
procedure TWDSPEngine.OpenAnalyzer;
|
||||
var
|
||||
Success: Integer;
|
||||
MaxW: Integer;
|
||||
Ovrlp: Integer;
|
||||
FRAME_RATE: Integer;
|
||||
Success: Integer;
|
||||
begin
|
||||
if FAnalyzerOpen then Exit;
|
||||
|
||||
@@ -411,30 +526,41 @@ begin
|
||||
XCreateAnalyzer(DISP_ID, @Success, 16384, 1, 1, nil);
|
||||
if Success <> 0 then Exit;
|
||||
|
||||
FRAME_RATE := 20;
|
||||
// max_w: WDSP аллоцирует ring-buffer и FFT планы на max_w сэмплов.
|
||||
// Формула: fft_size + min(0.1*samplerate, 0.1*fft_size*fps)
|
||||
// = 4096 + min(19200, 8192) = 12288 → 1700ms инициализации!
|
||||
// Реально нам нужен только один FFT-кадр без усреднения по времени.
|
||||
// MaxW = sz = 4096 — минимально допустимое значение (должно быть ≥ sz).
|
||||
// piHPSDR использует именно sz без добавки keep_time.
|
||||
MaxW := 4096;
|
||||
Ovrlp := 0;
|
||||
|
||||
FFlp[0] := 0;
|
||||
SetAnalyzer(DISP_ID, 1, 1, 1, @FFlp[0], 4096, FAudioBufSize, 1, 14.0,
|
||||
Ovrlp, 0, 0.0, 0.0, SPECTRUM_PIXELS, 1, 0, 0.0, 0.0, MaxW);
|
||||
|
||||
SetDisplayDetectorMode(DISP_ID, 0, DETECTOR_MODE_AVERAGE);
|
||||
SetDisplayAverageMode(DISP_ID, 0, AVERAGE_MODE_LOG_RECURSIVE);
|
||||
SetDisplayNumAverage(DISP_ID, 0, Max(2, Trunc(FRAME_RATE * 0.12)));
|
||||
SetDisplayAvBackmult(DISP_ID, 0, 0.45);
|
||||
SetDisplaySampleRate(DISP_ID, FSampleRate);
|
||||
SetDisplayNormOneHz(DISP_ID, 0, 0);
|
||||
ApplyAnalyzerSettings;
|
||||
|
||||
FAnalyzerOpen := True;
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.ApplyAnalyzerSettings;
|
||||
// Вызывается из OpenAnalyzer и при изменении FFT/window/detector/avg параметров
|
||||
var
|
||||
MaxW: Integer;
|
||||
begin
|
||||
// MaxW = FFTSize — минимально допустимо (piHPSDR approach)
|
||||
MaxW := FFFTSize;
|
||||
|
||||
// n_pixout=2: pixout=0 — спектр, pixout=1 — водопад
|
||||
SetAnalyzer(DISP_ID, 2, 1, 1, @FFlp[0], FFFTSize, FAudioBufSize, FWindowType, 14.0,
|
||||
0, 0, 0.0, 0.0, SPECTRUM_PIXELS, 1, 0, 0.0, 0.0, MaxW);
|
||||
|
||||
// Спектр (pixout=0)
|
||||
SetDisplayDetectorMode(DISP_ID, 0, FSpecDetector);
|
||||
SetDisplayAverageMode(DISP_ID, 0, FSpecAvgMode);
|
||||
SetDisplayNumAverage(DISP_ID, 0, FSpecAvgCount);
|
||||
SetDisplayAvBackmult(DISP_ID, 0, FSpecBackmult);
|
||||
SetDisplayNormOneHz(DISP_ID, 0, 0);
|
||||
|
||||
// Водопад (pixout=1)
|
||||
SetDisplayDetectorMode(DISP_ID, 1, FWfDetector);
|
||||
SetDisplayAverageMode(DISP_ID, 1, FWfAvgMode);
|
||||
SetDisplayNumAverage(DISP_ID, 1, FWfAvgCount);
|
||||
SetDisplayAvBackmult(DISP_ID, 1, FWfBackmult);
|
||||
SetDisplayNormOneHz(DISP_ID, 1, 0);
|
||||
|
||||
SetDisplaySampleRate(DISP_ID, FSampleRate);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.CloseAnalyzer;
|
||||
begin
|
||||
if not FAnalyzerOpen then Exit;
|
||||
@@ -501,13 +627,16 @@ begin
|
||||
SetRXAPanelRun(RXA_CHAN, 1);
|
||||
SetChannelState(RXA_CHAN, 1, 0);
|
||||
|
||||
// TXA: mic @ FAudioRate → IQ @ FSampleRate (DUC rate)
|
||||
// out_size = FAudioBufSize * FSampleRate/FAudioRate = FBufSize (4096 @ 192kHz)
|
||||
// FTXOut уже имеет размер FBufSize*2 — подходит идеально
|
||||
OpenChannel(
|
||||
TXA_CHAN,
|
||||
FAudioBufSize,
|
||||
FAudioBufSize,
|
||||
FAudioRate,
|
||||
FAudioRate,
|
||||
FAudioRate,
|
||||
FSampleRate,
|
||||
1,
|
||||
0,
|
||||
0.010, 0.010, 0.010, 0.010,
|
||||
@@ -668,18 +797,19 @@ begin
|
||||
FRXIn[i * 2 + 1] := FRXAccQ[i];
|
||||
end;
|
||||
|
||||
// Копируем входные IQ ДО fexchange0 для Spectrum0
|
||||
// fexchange0 in-place перезапишет FRXIn выходными данными @ 48kHz
|
||||
// Копируем IQ в FSpecBuf ДО fexchange0: fexchange0 in-place перезаписывает FRXIn
|
||||
if FAnalyzerOpen then
|
||||
for i := 0 to FBufSize * 2 - 1 do
|
||||
FSpecBuf[i] := FRXIn[i]; // Single→Double, входные IQ @ 192kHz
|
||||
begin
|
||||
Move(FRXIn[0], FSpecBuf[0], FBufSize * 2 * SizeOf(Double));
|
||||
end;
|
||||
|
||||
// DSP обработка
|
||||
Err := 0;
|
||||
fexchange0(RXA_CHAN, @FRXIn[0], @FRXOut[0], @Err);
|
||||
|
||||
// Spectrum0 с входными данными (скопированными до fexchange0)
|
||||
if FAnalyzerOpen then
|
||||
// Подаём RX IQ в анализатор только когда не передаём.
|
||||
// Во время TX анализатор кормит ProcessTXBlock (TX сигнал на той же частоте).
|
||||
if FAnalyzerOpen and not FTXActive then
|
||||
Spectrum0(1, DISP_ID, 0, 0, @FSpecBuf[0]);
|
||||
|
||||
// S-meter: читаем ОДИН раз в DSP-колбэке и кешируем в FSMeter.
|
||||
@@ -973,11 +1103,92 @@ end;
|
||||
|
||||
procedure TWDSPEngine.SetTXRun(Run: Boolean);
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
FTXActive := Run;
|
||||
|
||||
if Run then
|
||||
SetChannelState(TXA_CHAN, 1, 0) // run, no delay
|
||||
else
|
||||
SetChannelState(TXA_CHAN, 0, 1); // stop, with slew
|
||||
begin
|
||||
if FInitialized then
|
||||
SetChannelState(TXA_CHAN, 1, 0); // run, no delay
|
||||
// Сброс mic ring buffer и запуск TX потока
|
||||
FTXMicHead := 0;
|
||||
FTXMicTail := 0;
|
||||
if not Assigned(FTXThread) then
|
||||
begin
|
||||
FTXThread := TTXDSPThread.Create(Self);
|
||||
FTXThread.Priority := tpHighest;
|
||||
end;
|
||||
end else begin
|
||||
if FInitialized then
|
||||
SetChannelState(TXA_CHAN, 0, 1); // stop, with slew
|
||||
// Останавливаем TX поток
|
||||
if Assigned(FTXThread) then
|
||||
begin
|
||||
FTXThread.Terminate;
|
||||
RTLEventSetEvent(FTXMicSem);
|
||||
FTXThread.WaitFor;
|
||||
FreeAndNil(FTXThread);
|
||||
end;
|
||||
FTXMicHead := 0;
|
||||
FTXMicTail := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.ProcessTXBlock;
|
||||
// Вызывается из TTXDSPThread — обрабатывает один блок FAudioBufSize mic сэмплов
|
||||
// через WDSP TXA, выдаёт FBufSize IQ пар @ FSampleRate в FOnTXIQ callback
|
||||
var
|
||||
i, tail: Integer;
|
||||
Err: Integer;
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
|
||||
// Читаем FAudioBufSize сэмплов из ring buffer → FTXIn
|
||||
tail := FTXMicTail;
|
||||
for i := 0 to FAudioBufSize - 1 do
|
||||
begin
|
||||
FTXIn[i * 2] := FTXMicRing[tail];
|
||||
FTXIn[i * 2 + 1] := 0.0; // Q = 0, mic — монофонический сигнал
|
||||
tail := (tail + 1) and (TX_MIC_RING - 1);
|
||||
end;
|
||||
FTXMicTail := tail; // Атомарно обновляем tail (один writer — TX thread)
|
||||
|
||||
Err := 0;
|
||||
fexchange0(TXA_CHAN, @FTXIn[0], @FTXOut[0], @Err);
|
||||
|
||||
// TX сигнал → анализатор: спектр/водопад показывают переданный сигнал
|
||||
// FTXOut: FBufSize IQ пар @ FSampleRate — тот же формат что и FSpecBuf
|
||||
if FAnalyzerOpen then
|
||||
Spectrum0(1, DISP_ID, 0, 0, @FTXOut[0]);
|
||||
|
||||
// FTXOut содержит FBufSize IQ пар @ FSampleRate (interleaved double)
|
||||
if Assigned(FOnTXIQ) then
|
||||
FOnTXIQ(FTXOut, FBufSize);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.PushTXMicSamples16(const Src: array of SmallInt; N: Integer);
|
||||
// Вызывается из receive thread — lock-free, кладёт сэмплы в ring buffer
|
||||
// 16-bit big-endian samples из аппаратного микрофона
|
||||
var
|
||||
i, cnt, newHead: Integer;
|
||||
S: SmallInt;
|
||||
begin
|
||||
cnt := N;
|
||||
if cnt > Length(Src) then cnt := Length(Src);
|
||||
|
||||
for i := 0 to cnt - 1 do
|
||||
begin
|
||||
// Аппаратный mic приходит в big-endian, нужно swap байт
|
||||
S := SmallInt(((Src[i] and $FF) shl 8) or ((Src[i] shr 8) and $FF));
|
||||
newHead := (FTXMicHead + 1) and (TX_MIC_RING - 1);
|
||||
if newHead <> FTXMicTail then // буфер не полон
|
||||
begin
|
||||
FTXMicRing[FTXMicHead] := S / 32768.0;
|
||||
FTXMicHead := newHead;
|
||||
end;
|
||||
// если буфер полон — сэмпл отбрасывается (overrun)
|
||||
end;
|
||||
|
||||
RTLEventSetEvent(FTXMicSem); // будим TX поток
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -985,9 +1196,12 @@ end;
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
procedure TWDSPEngine.UpdateSpectrum;
|
||||
// Вызывается из таймера главного потока (~20fps)
|
||||
// Spectrum0 уже вызван в сетевом потоке после каждого fexchange0
|
||||
// Здесь только читаем готовые пиксели
|
||||
// Вызывается из таймера главного потока (настраиваемый FPS).
|
||||
// Здесь вызываем Spectrum0 — WDSP берёт снапшот из своего кольцевого
|
||||
// буфера (который fexchange0 непрерывно заполняет из DSP-потока).
|
||||
// Это обеспечивает равномерный FPS независимо от размера FFT:
|
||||
// при FFT=16384 WDSP использует уже накопленные данные из overlap-save
|
||||
// буфера, а не ждёт следующего полного блока.
|
||||
var
|
||||
PixBuf: array[0..SPECTRUM_PIXELS - 1] of Single;
|
||||
Flag: Integer;
|
||||
@@ -995,15 +1209,27 @@ var
|
||||
begin
|
||||
if not FInitialized or not FAnalyzerOpen then Exit;
|
||||
|
||||
// Спектр (pixout=0)
|
||||
Flag := 0;
|
||||
GetPixels(DISP_ID, 0, @PixBuf[0], @Flag);
|
||||
if Flag = 0 then Exit; // нет нового кадра — ждём следующего тика
|
||||
if Flag <> 0 then
|
||||
begin
|
||||
for i := 0 to SPECTRUM_PIXELS - 1 do
|
||||
FSpectrumPixels[i] := PixBuf[i];
|
||||
if Assigned(FOnSpectrum) then
|
||||
FOnSpectrum(FSpectrumPixels, SPECTRUM_PIXELS);
|
||||
end;
|
||||
|
||||
for i := 0 to SPECTRUM_PIXELS - 1 do
|
||||
FSpectrumPixels[i] := PixBuf[i];
|
||||
|
||||
if Assigned(FOnSpectrum) then
|
||||
FOnSpectrum(FSpectrumPixels, SPECTRUM_PIXELS);
|
||||
// Водопад (pixout=1)
|
||||
Flag := 0;
|
||||
GetPixels(DISP_ID, 1, @PixBuf[0], @Flag);
|
||||
if Flag <> 0 then
|
||||
begin
|
||||
for i := 0 to SPECTRUM_PIXELS - 1 do
|
||||
FWaterfallPixels[i] := PixBuf[i];
|
||||
if Assigned(FOnWaterfall) then
|
||||
FOnWaterfall(FWaterfallPixels, SPECTRUM_PIXELS);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.GetSpectrumData(var Pixels: array of Single;
|
||||
@@ -1024,4 +1250,50 @@ begin
|
||||
Result := FSMeter;
|
||||
end;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Настройки анализатора (применяются немедленно)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
procedure TWDSPEngine.SetFFTParams(FFTSize, WinType: Integer);
|
||||
begin
|
||||
// Ограничения: FFT size должен быть >= bf_sz (FAudioBufSize = 1024)
|
||||
// и степенью двойки, не превышать m_size=16384 (из XCreateAnalyzer)
|
||||
if FFTSize < FAudioBufSize then FFTSize := FAudioBufSize;
|
||||
if FFTSize > 16384 then FFTSize := 16384;
|
||||
if WinType < 0 then WinType := 0;
|
||||
if WinType > 6 then WinType := 6;
|
||||
FFFTSize := FFTSize;
|
||||
FWindowType := WinType;
|
||||
if FAnalyzerOpen then
|
||||
ApplyAnalyzerSettings;
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.SetSpectrumDisplay(Detector, AvgMode, AvgCount: Integer;
|
||||
Backmult: Double);
|
||||
begin
|
||||
FSpecDetector := Detector;
|
||||
FSpecAvgMode := AvgMode;
|
||||
FSpecAvgCount := AvgCount;
|
||||
FSpecBackmult := Backmult;
|
||||
if not FAnalyzerOpen then Exit;
|
||||
SetDisplayDetectorMode(DISP_ID, 0, FSpecDetector);
|
||||
SetDisplayAverageMode(DISP_ID, 0, FSpecAvgMode);
|
||||
SetDisplayNumAverage(DISP_ID, 0, FSpecAvgCount);
|
||||
SetDisplayAvBackmult(DISP_ID, 0, FSpecBackmult);
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.SetWaterfallDisplay(Detector, AvgMode, AvgCount: Integer;
|
||||
Backmult: Double);
|
||||
begin
|
||||
FWfDetector := Detector;
|
||||
FWfAvgMode := AvgMode;
|
||||
FWfAvgCount := AvgCount;
|
||||
FWfBackmult := Backmult;
|
||||
if not FAnalyzerOpen then Exit;
|
||||
SetDisplayDetectorMode(DISP_ID, 1, FWfDetector);
|
||||
SetDisplayAverageMode(DISP_ID, 1, FWfAvgMode);
|
||||
SetDisplayNumAverage(DISP_ID, 1, FWfAvgCount);
|
||||
SetDisplayAvBackmult(DISP_ID, 1, FWfBackmult);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user