Skip to content

Audio Streaming

Audio RX/TX via the Icom audio UDP port (default 50003).

AudioStream

icom_lan.audio.AudioStream

Manages audio RX/TX on the Icom audio UDP port.

Uses an :class:IcomTransport for the underlying UDP communication (discovery, pings, retransmit). Audio-specific packet framing is handled here.

Parameters:

Name Type Description Default
transport IcomTransport

Connected IcomTransport for the audio port.

required

Example::

stream = AudioStream(audio_transport)
await stream.start_rx(my_callback)
# ... later
await stream.stop_rx()

state property

Current audio stream state.

transport property

Underlying transport.

push_tx(opus_data) async

Send an Opus-encoded audio frame to the radio.

Parameters:

Name Type Description Default
opus_data bytes

Opus-encoded audio data.

required

Raises:

Type Description
RuntimeError

If not in transmitting state.

start_rx(callback, *, jitter_depth=None) async

Start receiving audio from the radio.

Parameters:

Name Type Description Default
callback Callable[[AudioPacket], None]

Called with each decoded :class:AudioPacket. When jitter buffering is enabled, None may be passed for gap placeholders (missing packets).

required
jitter_depth int | None

Override jitter buffer depth (0 to disable). Defaults to the value set at construction time.

None

Raises:

Type Description
RuntimeError

If already receiving or transmitting.

start_tx() async

Start transmitting audio to the radio.

Can be called while already receiving (full-duplex).

Raises:

Type Description
RuntimeError

If already transmitting.

stop_rx() async

Stop receiving audio and flush remaining buffered packets.

stop_tx() async

Stop transmitting audio.

If RX is still active, state reverts to RECEIVING.

AudioPacket

icom_lan.audio.AudioPacket dataclass

Parsed audio packet.

Attributes:

Name Type Description
ident int

Audio stream identifier (0x0080 for TX, varies for RX).

send_seq int

Audio-level sequence number.

data bytes

Opus-encoded audio data (raw bytes after header).

AudioState

icom_lan.audio.AudioState

Bases: StrEnum

Audio stream state.

JitterBuffer

icom_lan.audio.JitterBuffer

Reorder-and-delay buffer for incoming audio packets.

Collects packets and delivers them in sequence-number order after a configurable depth of buffering. Handles out-of-order packets, duplicates, and gaps (delivering None for missing packets).

Parameters:

Name Type Description Default
depth int

Number of packets to buffer before delivery (default 5, which is ~100 ms at 20 ms/packet).

5

Example::

jb = JitterBuffer(depth=5)
for pkt in jb.push(audio_packet):
    if pkt is None:
        # gap — insert silence
        ...
    else:
        play(pkt.data)

depth property

Configured buffer depth (number of packets).

pending property

Number of packets currently held in the buffer.

flush()

Flush all buffered packets in order (for stream end).

Returns:

Type Description
list[AudioPacket | None]

Remaining packets in order (None for gaps).

push(packet)

Insert a packet and return any packets ready for delivery.

Packets are delivered in order. If a gap is detected (missing sequence number), None is yielded in its place.

Parameters:

Name Type Description Default
packet AudioPacket

Incoming audio packet.

required

Returns:

Type Description
list[AudioPacket | None]

List of packets (or None for gaps) ready for playback.

list[AudioPacket | None]

May be empty if more buffering is needed.

Packet Functions

icom_lan.audio.parse_audio_packet(data)

Parse a raw UDP audio packet into an :class:AudioPacket.

Parameters:

Name Type Description Default
data bytes

Raw UDP packet bytes (must be > 0x18 bytes).

required

Returns:

Type Description
AudioPacket | None

Parsed AudioPacket, or None if the packet is too short or

AudioPacket | None

is a control/retransmit packet (type != DATA).

icom_lan.audio.build_audio_packet(opus_data, *, sender_id, receiver_id, send_seq, ident=TX_IDENT)

Build a raw UDP audio packet from Opus data.

Parameters:

Name Type Description Default
opus_data bytes

Opus-encoded audio frame.

required
sender_id int

Our connection ID.

required
receiver_id int

Radio's connection ID.

required
send_seq int

Audio-level sequence number.

required
ident int

Audio ident field (default TX_IDENT=0x0080).

TX_IDENT

Returns:

Type Description
bytes

Complete UDP packet bytes ready to send.

Usage

RX Audio (callback-based)

async with IcomRadio("192.168.1.100", username="u", password="p") as radio:
    received = []

    def on_audio(pkt):
        if pkt is not None:  # None = gap (missing packet)
            received.append(pkt.data)

    await radio.start_audio_rx(on_audio)
    await asyncio.sleep(10)
    await radio.stop_audio_rx()

TX Audio (push-based)

async with IcomRadio("192.168.1.100", username="u", password="p") as radio:
    await radio.start_audio_tx()
    await radio.push_audio_tx(opus_frame)
    await radio.stop_audio_tx()

Full-Duplex

async with IcomRadio("192.168.1.100", username="u", password="p") as radio:
    await radio.start_audio(rx_callback=on_audio, tx_enabled=True)
    # ... push TX frames, receive RX via callback ...
    await radio.stop_audio()

Codec Selection

from icom_lan import IcomRadio, AudioCodec

radio = IcomRadio(
    "192.168.1.100",
    audio_codec=AudioCodec.PCM_1CH_16BIT,  # default
    audio_sample_rate=48000,
)

Opus codecs

OPUS_1CH (0x40) and OPUS_2CH (0x41) are only supported when the radio reports connection_type == "WFVIEW". Standard connections use LPCM16 (0x04).