Skip to content

fix(voice): send real batch_len, not hardcoded BATCH_BYTES=82, on the call wire#41

Open
torlando-tech wants to merge 1 commit into
mainfrom
fix/voice-call-batch-len
Open

fix(voice): send real batch_len, not hardcoded BATCH_BYTES=82, on the call wire#41
torlando-tech wants to merge 1 commit into
mainfrom
fix/voice-call-batch-len

Conversation

@torlando-tech

Copy link
Copy Markdown
Owner

call_send_audio_batch shipped a fixed 82-byte bin8 payload + length, correct only for 8-byte/frame Codec2 modes (3200/1600). The shipping ULBW default is Codec2-700C at 4 B/frame → a real batch of 42 B, so every 700C packet carried 40 B of uninitialized stack and a bin8 length byte that lied (82 vs 42).

Symptom: pyxis↔pyxis decoded to silence (RX 6400 samples > the 5120 output guard → ring underrun every packet); pyxis → a length-driven peer (Columba/Python) decoded 10 real + 10 stack-noise frames = audible garbage.

Fix: use the batch_len the caller already computes. ULBW stays the default (LoRa-first). The most impactful of the voice-investigation fixes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UWZuYkHBRqNb6BZHV8sTG5

… call wire

call_send_audio_batch shipped a fixed 82-byte bin8 payload+length, correct only for 8-B/frame Codec2 (3200/1600). The ULBW default is 700C at 4 B/frame -> a 42-B batch, so every packet carried 40 B of uninitialized stack and a bin8 length that lied (82 vs 42): pyxis<->pyxis decoded to SILENCE (RX 6400 > 5120 output guard -> ring underrun), pyxis->a length-driven peer (Columba/Python) decoded 10 real + 10 stack-noise frames = audible GARBAGE. Use the batch_len the caller already computes. ULBW stays default (LoRa-first).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01UWZuYkHBRqNb6BZHV8sTG5
@greptile-apps

greptile-apps Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a hardcoded BATCH_BYTES=82 constant in call_send_audio_batch that was sized for Codec2-3200/1600 (8 bytes/frame) but incorrect for the shipping default Codec2-700C (4 bytes/frame, real batch 42 B). The fix threads the caller-computed batch_len through to all memcpy and bin8 length byte writes.

  • Single-batch path: bin8 length byte and memcpy now use batch_len (was always 82, which leaked 40 bytes of uninitialized stack into every 700C packet).
  • Multi-batch path: per-batch stride b * batch_len is also corrected; previously b * BATCH_BYTES would have skipped to the wrong offset for any mode whose frame size ≠ 8 B.

Confidence Score: 4/5

The change is a targeted, correct fix to a real on-wire corruption bug; the only call site always passes batch_count=1 and a buffer capped well under the 256-byte packet_buf.

The patch narrows a mismatched memcpy from 82 to the actual computed batch_len, fixing both the stack-data leak and the lying bin8 length byte. The single call site (pump_call_tx) always passes batch_count=1, so the multi-batch branch remains untested in production — it would overflow packet_buf[256] for large batch_count values, but that code path predates this PR and is unchanged here. The debug total_frames calculation (encoded_len/8) still gives a wrong frame count for 700C, but only affects the log line.

lib/tdeck_ui/UI/LXMF/UIManager.cpp — the multi-batch branch has a pre-existing latent overflow if batch_count is ever raised above a handful of entries; worth noting for a future follow-up but is not affected by this change.

Important Files Changed

Filename Overview
lib/tdeck_ui/UI/LXMF/UIManager.cpp Replaces hardcoded BATCH_BYTES=82 with the caller-supplied batch_len in both the single-batch and multi-batch encoding paths; fix is correct and the only call site always passes batch_count=1 with a buffer capped at 128 B, so no overflow risk is introduced.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Audio as LXSTAudio
    participant TX as pump_call_tx()
    participant Send as call_send_audio_batch()
    participant Wire as Reticulum Link

    Audio->>TX: "readEncodedPacket(encoded_buf, &encoded_len)"
    TX->>TX: "batch_data[0] = LXST_CODEC_CODEC2<br/>memcpy(batch_data+1, encoded_buf, encoded_len)<br/>batch_len = 1 + encoded_len"
    TX->>Send: "(batch_data, batch_len=42 for 700C, batch_count=1)"
    Note over Send: OLD: hardcoded BATCH_BYTES=82<br/>bin8 len=82, memcpy 82 B<br/>(40 B uninitialized stack leaked)
    Note over Send: NEW: use batch_len=42<br/>bin8 len=42, memcpy 42 B<br/>(exact payload, no leak)
    Send->>Send: "packet_buf[0]=0x81 fixmap(1)<br/>packet_buf[1]=0x01 key<br/>packet_buf[2]=0xC4 bin8<br/>packet_buf[3]=(uint8_t)batch_len<br/>memcpy(+4, batch_data, batch_len)"
    Send->>Wire: Packet(packet_buf, pos).send()
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Audio as LXSTAudio
    participant TX as pump_call_tx()
    participant Send as call_send_audio_batch()
    participant Wire as Reticulum Link

    Audio->>TX: "readEncodedPacket(encoded_buf, &encoded_len)"
    TX->>TX: "batch_data[0] = LXST_CODEC_CODEC2<br/>memcpy(batch_data+1, encoded_buf, encoded_len)<br/>batch_len = 1 + encoded_len"
    TX->>Send: "(batch_data, batch_len=42 for 700C, batch_count=1)"
    Note over Send: OLD: hardcoded BATCH_BYTES=82<br/>bin8 len=82, memcpy 82 B<br/>(40 B uninitialized stack leaked)
    Note over Send: NEW: use batch_len=42<br/>bin8 len=42, memcpy 42 B<br/>(exact payload, no leak)
    Send->>Send: "packet_buf[0]=0x81 fixmap(1)<br/>packet_buf[1]=0x01 key<br/>packet_buf[2]=0xC4 bin8<br/>packet_buf[3]=(uint8_t)batch_len<br/>memcpy(+4, batch_data, batch_len)"
    Send->>Wire: Packet(packet_buf, pos).send()
Loading

Reviews (1): Last reviewed commit: "fix(voice): send real batch_len, not har..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant