mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
feat(slices): per-pan tuning wheel and per-slice RX mute on TX
Mouse wheel over an extra panadapter now retunes that pan's primary slice (first created on that receiver) instead of the main VFO; a slice flag under the cursor still wins, pan 0 keeps driving the VFO. Slice flags get a clickable RXM badge: RX audio of the slice is muted while transmitting (default), so the operator no longer hears himself on every slice in full duplex. State persists with the workspace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -158,6 +158,7 @@ type
|
||||
AGC: TWDSPAGCMode;
|
||||
Volume: Double; // 0..1
|
||||
Muted: Boolean;
|
||||
RxMuteOnTx: Boolean; // True (умолч.) = динамик слайса молчит на передаче
|
||||
FMSQOn: Boolean; // FM-squelch включён (слайс в режиме FM)
|
||||
FMSQLevel: Integer; // порог squelch 0..100
|
||||
DeviceIndex: Integer; // PortAudio-индекс (-1 = default)
|
||||
@@ -187,6 +188,7 @@ type
|
||||
const Left, Right: array of Single; Count: Integer);
|
||||
procedure OnSliceDMRAudioReady(SliceId: Integer;
|
||||
const PCM: array of Single; Count: Integer);
|
||||
function SliceMutedByTx(Idx: Integer): Boolean; // слайс молчит на передаче?
|
||||
procedure RefreshSliceShifts; // пересчёт ShiftHz слайсов пана 0 при смене центра
|
||||
// --- Панадаптеры на аппаратных DDC (этап 3.1/3.2) ---
|
||||
procedure ResetPanDDCMaps; // все пан-слоты свободны, DDC→Pan пуст
|
||||
@@ -592,6 +594,8 @@ type
|
||||
procedure SetSliceAGCMode(Id: Integer; AGC: TWDSPAGCMode);
|
||||
procedure SetSliceVolume(Id: Integer; Vol: Double); // 0..1
|
||||
procedure SetSliceMute(Id: Integer; Mute: Boolean);
|
||||
procedure SetSliceRxMuteOnTx(Id: Integer; On_: Boolean); // self-monitor слайса на TX
|
||||
function PanPrimarySliceId(PanId: Integer): Integer; // первый созданный слайс пана
|
||||
procedure SetSliceDSP(Id, NRMode, NBMode: Integer; SNBOn, ANFOn: Boolean);
|
||||
procedure SetSliceFMSquelch(Id: Integer; On_: Boolean; Level: Integer);
|
||||
function SliceSMeter(Id: Integer): Double; // dBm, из DSP-потока
|
||||
@@ -1334,10 +1338,19 @@ begin
|
||||
idx := FindSliceIndex(SliceId);
|
||||
if idx < 0 then Exit;
|
||||
if FSlices[idx].Mode = MODE_DMR then Exit;
|
||||
if SliceMutedByTx(idx) then Exit;
|
||||
if FSlices[idx].Audio <> nil then
|
||||
FSlices[idx].Audio.Write(Left, Right, Count);
|
||||
end;
|
||||
|
||||
// Слайс глушится на передаче? В full-duplex (Pluto/QO-100) RX-тракт живёт и во
|
||||
// время TX, поэтому иначе оператор слышит сам себя на КАЖДОМ слайсе. У главного
|
||||
// приёмника за это отвечает FRxMuteOnTx (кнопка RX MUTE), у слайса — свой флаг.
|
||||
function TRadioController.SliceMutedByTx(Idx: Integer): Boolean;
|
||||
begin
|
||||
Result := FTransmitting and FSlices[Idx].RxMuteOnTx;
|
||||
end;
|
||||
|
||||
// DSP thread: flat, volume-independent discriminator of the corresponding
|
||||
// WDSP slice channel. FeedAudio only copies it into that decoder's ring.
|
||||
procedure TRadioController.OnSliceDemodAudioReady(SliceId: Integer;
|
||||
@@ -1362,6 +1375,7 @@ begin
|
||||
idx := FindSliceIndex(SliceId);
|
||||
if (idx < 0) or (FSlices[idx].Mode <> MODE_DMR) or
|
||||
FSlices[idx].Muted or not Assigned(FSlices[idx].Audio) then Exit;
|
||||
if SliceMutedByTx(idx) then Exit;
|
||||
N := Min(Count, Length(PCM));
|
||||
SetLength(Left, N * 6);
|
||||
SetLength(Right, N * 6);
|
||||
@@ -1783,6 +1797,7 @@ begin
|
||||
FSlices[slot].AGC := AGC;
|
||||
FSlices[slot].Volume := Vol;
|
||||
FSlices[slot].Muted := False;
|
||||
FSlices[slot].RxMuteOnTx := True; // себя на передаче по умолчанию не слышим
|
||||
FSlices[slot].FMSQOn := False; // squelch по умолчанию выключен
|
||||
FSlices[slot].FMSQLevel := 40;
|
||||
FSlices[slot].DeviceIndex := DevIndex;
|
||||
@@ -1878,6 +1893,26 @@ begin
|
||||
if Assigned(FDSPEngine) then FDSPEngine.SetSliceMute(Id, Mute);
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetSliceRxMuteOnTx(Id: Integer; On_: Boolean);
|
||||
var idx: Integer;
|
||||
begin
|
||||
idx := FindSliceIndex(Id);
|
||||
if idx < 0 then Exit;
|
||||
FSlices[idx].RxMuteOnTx := On_;
|
||||
end;
|
||||
|
||||
// «Главный» слайс пана = созданный на нём первым. Id монотонно растёт
|
||||
// (FNextSliceId), поэтому первый = слайс с минимальным Id. 0 = слайсов нет.
|
||||
function TRadioController.PanPrimarySliceId(PanId: Integer): Integer;
|
||||
var i: Integer;
|
||||
begin
|
||||
Result := 0;
|
||||
for i := 0 to MAX_SLICES - 1 do
|
||||
if FSlices[i].Active and (FSlices[i].PanId = PanId) and
|
||||
((Result = 0) or (FSlices[i].Id < Result)) then
|
||||
Result := FSlices[i].Id;
|
||||
end;
|
||||
|
||||
procedure TRadioController.SetSliceDSP(Id, NRMode, NBMode: Integer;
|
||||
SNBOn, ANFOn: Boolean);
|
||||
var idx: Integer;
|
||||
|
||||
Reference in New Issue
Block a user