feat(neswire): open the audio server

Captures a session's audio and hands it to the transport over a local socket.
Third component in, imported as a tree from `nestrilabs/neswire` on the same
terms as the previous two.

Wired to the workspace, `nesprotocol` by path. 4 tests pass.

`bin/hub-stub.rs` is a stand-in for the transport's listener, which is what lets
this be developed and tested without the rest of a box existing. It names the
transport by its old name, and is left for the rename commit along with the two
in the compositor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wanjohi
2026-08-26 18:02:38 +03:00
parent 37dd985810
commit 06b844b961
9 changed files with 1301 additions and 1 deletions

58
apps/neswire/src/main.rs Normal file
View File

@@ -0,0 +1,58 @@
mod encoder;
mod sink;
use clap::Parser;
#[derive(Parser, Debug)]
struct Args {
/// Path for the audio IPC socket (neswire → nestri-guest-hub)
#[arg(
long,
env = "NESWIRE_IPC_PATH",
default_value = "/tmp/nestri-audio.sock"
)]
ipc_path: String,
/// Output channels: 2, 6, or 8
#[arg(long, env = "NESWIRE_CHANNELS", default_value_t = 2)]
channels: u32,
/// Packet duration in ms (5, 10..)
#[arg(long, env = "NESWIRE_PACKET_DURATION_MS", default_value_t = 5)]
packet_duration_ms: u32,
/// Bitrate per channel in kbps
#[arg(long, env = "NESWIRE_BITRATE_PER_CHANNEL", default_value_t = 64)]
bitrate_per_channel: u32,
}
fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
let args = Args::parse();
let sample_rate = 48_000u32;
let frame_size = (sample_rate * args.packet_duration_ms) / 1000;
let (tx, rx) = crossbeam_channel::bounded::<Vec<f32>>(256);
let encoder_config = encoder::EncoderConfig {
channels: args.channels,
sample_rate,
frame_size,
ipc_path: args.ipc_path,
bitrate_per_channel: args.bitrate_per_channel,
};
std::thread::spawn(move || {
if let Err(e) = encoder::run(encoder_config, rx) {
tracing::error!("encoder thread died: {e:#}");
}
});
sink::run(sample_rate, args.channels, frame_size, tx)?;
Ok(())
}