From 771cbd19874970ba0ceeb465ac4b1d10e9be1063 Mon Sep 17 00:00:00 2001 From: Stanislaw Grams Date: Sat, 7 Feb 2026 14:21:42 +0100 Subject: [PATCH] [feat](trx-core): add audio protocol types and framing helpers Add audio streaming wire protocol: message type constants (StreamInfo, RxFrame, TxFrame), AudioStreamInfo struct, and async read/write helpers for length-prefixed frames over AsyncRead/AsyncWrite. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Stanislaw Grams --- src/trx-core/src/audio.rs | 54 +++++++++++++++++++++++++++++++++++++++ src/trx-core/src/lib.rs | 1 + 2 files changed, 55 insertions(+) create mode 100644 src/trx-core/src/audio.rs diff --git a/src/trx-core/src/audio.rs b/src/trx-core/src/audio.rs new file mode 100644 index 0000000..831e3b8 --- /dev/null +++ b/src/trx-core/src/audio.rs @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2025 Stanislaw Grams +// +// SPDX-License-Identifier: BSD-2-Clause + +//! Audio streaming protocol types and framing helpers. +//! +//! Wire format: `[1 byte type][4 bytes BE length N][N bytes payload]` + +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; + +pub const AUDIO_MSG_STREAM_INFO: u8 = 0x00; +pub const AUDIO_MSG_RX_FRAME: u8 = 0x01; +pub const AUDIO_MSG_TX_FRAME: u8 = 0x02; + +/// Maximum payload size (1 MB) to reject bogus frames early. +const MAX_PAYLOAD_SIZE: u32 = 1_048_576; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct AudioStreamInfo { + pub sample_rate: u32, + pub channels: u8, + pub frame_duration_ms: u16, +} + +/// Write a length-prefixed audio message. +pub async fn write_audio_msg( + writer: &mut W, + msg_type: u8, + payload: &[u8], +) -> std::io::Result<()> { + let len = payload.len() as u32; + writer.write_u8(msg_type).await?; + writer.write_u32(len).await?; + writer.write_all(payload).await?; + writer.flush().await?; + Ok(()) +} + +/// Read one length-prefixed audio message, returning `(type, payload)`. +pub async fn read_audio_msg( + reader: &mut R, +) -> std::io::Result<(u8, Vec)> { + let msg_type = reader.read_u8().await?; + let len = reader.read_u32().await?; + if len > MAX_PAYLOAD_SIZE { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("audio frame too large: {} bytes", len), + )); + } + let mut payload = vec![0u8; len as usize]; + reader.read_exact(&mut payload).await?; + Ok((msg_type, payload)) +} diff --git a/src/trx-core/src/lib.rs b/src/trx-core/src/lib.rs index 1c77665..07346d3 100644 --- a/src/trx-core/src/lib.rs +++ b/src/trx-core/src/lib.rs @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: BSD-2-Clause +pub mod audio; pub mod client; pub mod math; pub mod radio;