mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
add browser mic TX: Opus-encoded mic audio from web client to WDSP TX chain
- Browser: AudioWorklet accumulates 960-sample frames, AudioEncoder encodes to Opus, sends binary WS frame 'M' + raw Opus packet - Server: opcode $02 handler decodes Opus via libopus, calls OnWebMic callback - MainForm: WebOnMic converts PSingle→Double, pushes to PushTXMicSamplesD; ApplyMOX switches mic source to txmsWeb when web client active, restores on TX-off - WDSPEngine: added txmsWeb to TTXMicSource enum Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -103,6 +103,13 @@ type
|
||||
TOpus_encoder_ctl_set = function(st: POpusEncoder;
|
||||
request: Integer; value: Integer): Integer; cdecl;
|
||||
|
||||
POpusDecoder = Pointer;
|
||||
TOpus_decoder_create = function(Fs, channels: Integer;
|
||||
error: PInteger): POpusDecoder; cdecl;
|
||||
TOpus_decode_float = function(st: POpusDecoder; data: PByte; len: Integer;
|
||||
pcm: PSingle; frame_size, decode_fec: Integer): Integer; cdecl;
|
||||
TOpus_decoder_destroy = procedure(st: POpusDecoder); cdecl;
|
||||
|
||||
// ── Callbacks в MainForm ──────────────────────────────────────────────────
|
||||
TWebCmdFreq = procedure(Hz: Double) of object;
|
||||
TWebCmdMode = procedure(Mode: Integer) of object;
|
||||
@@ -129,6 +136,7 @@ type
|
||||
TWebCmdFreqA = procedure(Hz: Double) of object;
|
||||
TWebCmdAttn = procedure(Idx: Integer) of object;
|
||||
TWebCmdTun = procedure(On_: Boolean) of object;
|
||||
TWebMicCB = procedure(Samples: PSingle; Count: Integer) of object;
|
||||
|
||||
// ── Главный класс сервера ─────────────────────────────────────────────────
|
||||
TWebServer = class
|
||||
@@ -144,6 +152,12 @@ type
|
||||
FOpusBufPos: Integer;
|
||||
FOpusOut: array[0..3999] of Byte;
|
||||
FOpusReady: Boolean;
|
||||
// Opus decoder — для RX TX-mic аудио от браузера
|
||||
FOpusDec: POpusDecoder;
|
||||
FOpusDecCreate: TOpus_decoder_create;
|
||||
FOpusDecDecode: TOpus_decode_float;
|
||||
FOpusDecDestroy: TOpus_decoder_destroy;
|
||||
FOnWebMic: TWebMicCB;
|
||||
|
||||
// ── TCP ──
|
||||
FListenSock: TSocket;
|
||||
@@ -292,6 +306,7 @@ type
|
||||
property OnFreqA: TWebCmdFreqA read FOnFreqA write FOnFreqA;
|
||||
property OnAttn: TWebCmdAttn read FOnAttn write FOnAttn;
|
||||
property OnTun: TWebCmdTun read FOnTun write FOnTun;
|
||||
property OnWebMic: TWebMicCB read FOnWebMic write FOnWebMic;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -423,6 +438,9 @@ begin
|
||||
FOpusDestroy := TOpus_encoder_destroy(GetProcAddress(FOpusLib, 'opus_encoder_destroy'));
|
||||
FOpusEncode := TOpus_encode_float( GetProcAddress(FOpusLib, 'opus_encode_float'));
|
||||
FOpusCtl := TOpus_encoder_ctl_set(GetProcAddress(FOpusLib, 'opus_encoder_ctl'));
|
||||
FOpusDecCreate := TOpus_decoder_create( GetProcAddress(FOpusLib, 'opus_decoder_create'));
|
||||
FOpusDecDecode := TOpus_decode_float( GetProcAddress(FOpusLib, 'opus_decode_float'));
|
||||
FOpusDecDestroy := TOpus_decoder_destroy(GetProcAddress(FOpusLib, 'opus_decoder_destroy'));
|
||||
|
||||
if not Assigned(FOpusCreate) or not Assigned(FOpusEncode) then
|
||||
begin
|
||||
@@ -439,6 +457,14 @@ begin
|
||||
if Assigned(FOpusCtl) then
|
||||
FOpusCtl(FOpusEnc, 4002, OPUS_BITRATE);
|
||||
|
||||
// Декодер для TX mic (браузер → Opus → WDSP)
|
||||
if Assigned(FOpusDecCreate) then
|
||||
begin
|
||||
Err := 0;
|
||||
FOpusDec := FOpusDecCreate(OPUS_SAMPLE_RATE, OPUS_CHANNELS, @Err);
|
||||
if Err <> 0 then FOpusDec := nil;
|
||||
end;
|
||||
|
||||
FOpusBufPos := 0;
|
||||
FOpusReady := True;
|
||||
Result := True;
|
||||
@@ -449,6 +475,9 @@ begin
|
||||
if FOpusReady and Assigned(FOpusDestroy) and (FOpusEnc <> nil) then
|
||||
FOpusDestroy(FOpusEnc);
|
||||
FOpusEnc := nil;
|
||||
if Assigned(FOpusDec) and Assigned(FOpusDecDestroy) then
|
||||
FOpusDecDestroy(FOpusDec);
|
||||
FOpusDec := nil;
|
||||
FOpusReady := False;
|
||||
if FOpusLib <> 0 then
|
||||
begin
|
||||
@@ -664,6 +693,8 @@ var
|
||||
Consumed: Integer;
|
||||
Raw: array[0..8191] of Byte;
|
||||
RawLen: Integer;
|
||||
PcmBuf: array[0..5759] of Single; // 120ms max @ 48kHz (для RX TX-mic)
|
||||
Decoded: Integer;
|
||||
begin
|
||||
WsHandled := False;
|
||||
RawLen := 0;
|
||||
@@ -813,6 +844,18 @@ begin
|
||||
if PayLen > 0 then Move(Payload[0], Header[1], PayLen);
|
||||
ProcessCommand(Client, Header);
|
||||
end;
|
||||
$02: // Binary — TX mic Opus frame: byte 'M' + raw Opus packet
|
||||
begin
|
||||
if (PayLen > 1) and (Payload[0] = Ord('M')) and
|
||||
Assigned(FOpusDec) and Assigned(FOpusDecDecode) and
|
||||
Assigned(FOnWebMic) then
|
||||
begin
|
||||
Decoded := FOpusDecDecode(FOpusDec, @Payload[1], PayLen - 1,
|
||||
@PcmBuf[0], Length(PcmBuf), 0);
|
||||
if Decoded > 0 then
|
||||
FOnWebMic(@PcmBuf[0], Decoded);
|
||||
end;
|
||||
end;
|
||||
$08: // Close
|
||||
begin
|
||||
Client.State := wsClosed;
|
||||
|
||||
Reference in New Issue
Block a user