feat(dmr): decode and route AMBE voice

This commit is contained in:
2026-07-14 18:55:28 +03:00
parent e0e74b5337
commit 28c434d9a7
7 changed files with 231 additions and 10 deletions
+42 -1
View File
@@ -405,6 +405,7 @@ type
// вызывающим (GUI или демон) после CreateEngines.
procedure OnAudioReady(const Left, Right: array of Single; Count: Integer);
procedure OnDemodAudioReady(const Left, Right: array of Single; Count: Integer);
procedure OnDMRAudioReady(const PCM: array of Single; Count: Integer);
procedure OnSpectrumReady(const Pixels: array of Single; Count: Integer);
procedure OnWaterfallReady(const Pixels: array of Single; Count: Integer);
procedure OnMicPacket(const Data: TMicDataPacket);
@@ -851,6 +852,7 @@ begin
// DMR front-end owns a worker thread. It stays idle until MODE_DMR is active.
FDMRDec := TDMRDecoder.Create;
FDMRDec.OnAudio := OnDMRAudioReady;
// Audio out/in — объекты создаём сейчас, Open вызывается позже (после показа
// формы / при подключении устройства).
@@ -862,6 +864,13 @@ end;
procedure TRadioController.FreeEngines;
var i: Integer;
begin
// Stop the DMR worker before destroying any of its audio destinations.
if Assigned(FDMRDec) then
begin
FDMRDec.SetEnabled(False);
FDMRDec.OnAudio := nil;
FreeAndNil(FDMRDec);
end;
if Assigned(FNetwork) then
begin
if FNetwork.Connected then FNetwork.Disconnect;
@@ -883,7 +892,6 @@ begin
FDSPEngine.Close;
FreeAndNil(FDSPEngine);
end;
if Assigned(FDMRDec) then FreeAndNil(FDMRDec);
if Assigned(FBeaconDec) then FreeAndNil(FBeaconDec);
end;
@@ -1186,6 +1194,39 @@ begin
FDMRDec.SetEnabled(False);
end;
procedure TRadioController.OnDMRAudioReady(const PCM: array of Single;
Count: Integer);
// DMR worker thread, 8 kHz mono from mbelib. Linear 6x interpolation brings
// it to the common 48 kHz output route used by local, web and radio speakers.
var
Left, Right: array of Single;
i, Phase, OutPos, N: Integer;
A, B, V, Gain: Single;
begin
if (FMode <> MODE_DMR) or FMuted or (Count <= 0) then Exit;
N := Min(Count, Length(PCM));
SetLength(Left, N * 6);
SetLength(Right, N * 6);
Gain := FVolume / 100.0;
OutPos := 0;
for i := 0 to N - 1 do
begin
A := PCM[i];
if i + 1 < N then B := PCM[i + 1] else B := A;
for Phase := 0 to 5 do
begin
V := (A + (B - A) * (Phase / 6.0)) * Gain;
Left[OutPos] := V;
Right[OutPos] := V;
Inc(OutPos);
end;
end;
if FSendAudioToRadio and Assigned(FNetwork) then
FNetwork.SendSpeakerAudio(Left, Right, OutPos);
if Assigned(FOnAudioConsume) and FOnAudioConsume(Left, Right, OutPos) then Exit;
if FLocalAudio and Assigned(FAudioOut) then FAudioOut.Write(Left, Right, OutPos);
end;
// ---------------------------------------------------------------------------
// Мультислайсы (софт-fan-out)
// ---------------------------------------------------------------------------