From 7d76a0cd5bc1809996d9e743d59f9974a04be50d Mon Sep 17 00:00:00 2001 From: TerraMaster85 Date: Sun, 15 Dec 2024 23:52:17 -0500 Subject: [PATCH] tap beginnings, audio out. investigate samplerate bug --- Cargo.lock | 117 +++++++++++++++++++++++++++ Cargo.toml | 2 + src/audio_receiver/mod.rs | 24 +++--- src/audio_transmitter/mod.rs | 148 +++++++++++++++++++++++++++++++++++ src/dsp_inb/mod.rs | 4 +- src/main.rs | 39 +++++---- src/tap_junction/mod.rs | 25 ++++++ src/task_msg.rs | 3 +- src/wire_fmt/cap.rs | 2 +- 9 files changed, 332 insertions(+), 32 deletions(-) create mode 100644 src/audio_transmitter/mod.rs create mode 100644 src/tap_junction/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 717dc40..a8de58c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "clang-sys" version = "1.8.1" @@ -225,12 +231,97 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "dasp_envelope" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec617ce7016f101a87fe85ed44180839744265fae73bb4aa43e7ece1b7668b6" +dependencies = [ + "dasp_frame", + "dasp_peak", + "dasp_ring_buffer", + "dasp_rms", + "dasp_sample", +] + +[[package]] +name = "dasp_frame" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a3937f5fe2135702897535c8d4a5553f8b116f76c1529088797f2eee7c5cd6" +dependencies = [ + "dasp_sample", +] + +[[package]] +name = "dasp_interpolate" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc975a6563bb7ca7ec0a6c784ead49983a21c24835b0bc96eea11ee407c7486" +dependencies = [ + "dasp_frame", + "dasp_ring_buffer", + "dasp_sample", +] + +[[package]] +name = "dasp_peak" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf88559d79c21f3d8523d91250c397f9a15b5fc72fbb3f87fdb0a37b79915bf" +dependencies = [ + "dasp_frame", + "dasp_sample", +] + +[[package]] +name = "dasp_ring_buffer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07d79e19b89618a543c4adec9c5a347fe378a19041699b3278e616e387511ea1" + +[[package]] +name = "dasp_rms" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6c5dcb30b7e5014486e2822537ea2beae50b19722ffe2ed7549ab03774575aa" +dependencies = [ + "dasp_frame", + "dasp_ring_buffer", + "dasp_sample", +] + [[package]] name = "dasp_sample" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +[[package]] +name = "dasp_signal" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa1ab7d01689c6ed4eae3d38fe1cea08cba761573fbd2d592528d55b421077e7" +dependencies = [ + "dasp_envelope", + "dasp_frame", + "dasp_interpolate", + "dasp_peak", + "dasp_ring_buffer", + "dasp_rms", + "dasp_sample", + "dasp_window", +] + +[[package]] +name = "dasp_window" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ded7b88821d2ce4e8b842c9f1c86ac911891ab89443cc1de750cae764c5076" +dependencies = [ + "dasp_sample", +] + [[package]] name = "either" version = "1.13.0" @@ -429,6 +520,18 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -677,9 +780,11 @@ version = "0.1.0" dependencies = [ "anyhow", "cpal", + "dasp_signal", "ringbuf", "thiserror 2.0.6", "tokio", + "tokio-tun", "tracing", "tracing-subscriber", ] @@ -820,6 +925,18 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-tun" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bf48df2abbfd12d0b8495dcf960b125109493d85f2c787b44a2729a8022bc4b" +dependencies = [ + "libc", + "nix", + "thiserror 1.0.69", + "tokio", +] + [[package]] name = "toml_datetime" version = "0.6.8" diff --git a/Cargo.toml b/Cargo.toml index 24d3a50..6160da4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,10 @@ edition = "2021" [dependencies] anyhow = "1.0.94" cpal = "0.15.3" +dasp_signal = "0.11.0" ringbuf = "0.4.7" thiserror = "2.0.6" tokio = { version = '^1', features = ["full"] } +tokio-tun = "0.12.1" tracing = "0.1.41" tracing-subscriber = "0.3.19" diff --git a/src/audio_receiver/mod.rs b/src/audio_receiver/mod.rs index 11351f3..efeab11 100644 --- a/src/audio_receiver/mod.rs +++ b/src/audio_receiver/mod.rs @@ -14,7 +14,9 @@ pub fn audio_receiver_main( cpal_input: cpal::Device, ) -> Result<()> { trace!("audio_receiver started"); - let cpal_input_config = cpal_input.supported_input_configs()?.max_by_key(|cfg| { + let cpal_input_config = cpal_input.supported_input_configs() + .context("could not find a suitable audio input configuration. are you root, trying to use a normal user's audio stack? try setting the capability CAP_NET_ADMIN=ep on this binary, and running as the user who is running the audio stack.")? + .max_by_key(|cfg| { match cfg.sample_format() { SampleFormat::U8 => 10, SampleFormat::I8 => 11, @@ -47,58 +49,58 @@ pub fn audio_receiver_main( move |pcm, _: &_| take_input_pcm::(pcm, &tx_for_demod), failed_pcm_take, None, - )?, + ).context("failed to build input stream")?, cpal::SampleFormat::I8 => cpal_input.build_input_stream( &cpal_input_config.into(), move |pcm, _: &_| take_input_pcm::(pcm, &tx_for_demod), failed_pcm_take, None, - )?, + ).context("failed to build input stream")?, cpal::SampleFormat::I16 => cpal_input.build_input_stream( &cpal_input_config.into(), move |pcm, _: &_| take_input_pcm::(pcm, &tx_for_demod), failed_pcm_take, None, - )?, + ).context("failed to build input stream")?, cpal::SampleFormat::I32 => cpal_input.build_input_stream( &cpal_input_config.into(), move |pcm, _: &_| take_input_pcm::(pcm, &tx_for_demod), failed_pcm_take, None, - )?, + ).context("failed to build input stream")?, cpal::SampleFormat::F32 => cpal_input.build_input_stream( &cpal_input_config.into(), move |pcm, _: &_| take_input_pcm::(pcm, &tx_for_demod), failed_pcm_take, None, - )?, + ).context("failed to build input stream")?, cpal::SampleFormat::F64 => cpal_input.build_input_stream( &cpal_input_config.into(), move |pcm, _: &_| take_input_pcm::(pcm, &tx_for_demod), failed_pcm_take, None, - )?, + ).context("failed to build input stream")?, cpal::SampleFormat::U64 => cpal_input.build_input_stream( &cpal_input_config.into(), move |pcm, _: &_| take_input_pcm::(pcm, &tx_for_demod), failed_pcm_take, None, - )?, + ).context("failed to build input stream")?, cpal::SampleFormat::I64 => cpal_input.build_input_stream( &cpal_input_config.into(), move |pcm, _: &_| take_input_pcm::(pcm, &tx_for_demod), failed_pcm_take, None, - )?, + ).context("failed to build input stream")?, sample_format => { Err(anyhow::Error::msg( format!( - "unsupported sample format '{}'", sample_format) + "unsupported sample format '{}'! please ask developers to add it", sample_format) ))? } }; - input_stream.play()?; + input_stream.play().context("failed to start intake audio stream")?; Ok(()) } diff --git a/src/audio_transmitter/mod.rs b/src/audio_transmitter/mod.rs new file mode 100644 index 0000000..21eaa62 --- /dev/null +++ b/src/audio_transmitter/mod.rs @@ -0,0 +1,148 @@ +use anyhow::{Context, Result}; + +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use cpal::{FromSample, Sample, SampleFormat}; + +use dasp_signal::Signal; + +use tracing::{error, info, trace, warn}; + +use tokio::sync::mpsc::Receiver; + +use crate::task_msg::{PcmFrameMessage, PcmSampleMessage}; + +pub fn audio_transmitter_main( + mut rx_for_wire: Receiver, + cpal_output: cpal::Device, +) -> Result<()> { + trace!("audio_transmitter started"); + let cpal_output_config = cpal_output.supported_output_configs() + .context("could not find a suitable audio output configuration. are you root, trying to use a normal user's audio stack? try setting the capability CAP_NET_ADMIN=ep on this binary, and running as the user who is running the audio stack.")? + .max_by_key(|cfg| { + match cfg.sample_format() { + SampleFormat::U8 => 10, + SampleFormat::I8 => 11, + SampleFormat::U16 => 20, + SampleFormat::I16 => 21, + SampleFormat::U32 => 30, + SampleFormat::I32 => 31, + SampleFormat::F32 => 40, + SampleFormat::F64 => 50, + SampleFormat::U64 => 60, + SampleFormat::I64 => 61, + x => { warn!("Backend offered unknown sample format {x}",); -999 } + } + }).expect("output device has no supported configurations available") + .with_max_sample_rate(); + + info!( + "output samples are {} @ {}hz", + &cpal_output_config.sample_format(), + &cpal_output_config.sample_rate().0, + ); + + let failed_pcm_give = move |e| { + warn!("dropped some PCM data on output stream?! {}", e); + }; + + let mut i = 0f64; + + let output_stream = match cpal_output_config.sample_format() { + cpal::SampleFormat::U8 => cpal_output + .build_output_stream( + &cpal_output_config.into(), + move |pcm, _: &_| give_output_pcm::(pcm, &mut rx_for_wire, &mut i), + failed_pcm_give, + None, + ) + .context("failed to build output stream")?, + cpal::SampleFormat::I8 => cpal_output + .build_output_stream( + &cpal_output_config.into(), + move |pcm, _: &_| give_output_pcm::(pcm, &mut rx_for_wire, &mut i), + failed_pcm_give, + None, + ) + .context("failed to build output stream")?, + cpal::SampleFormat::I16 => cpal_output + .build_output_stream( + &cpal_output_config.into(), + move |pcm, _: &_| give_output_pcm::(pcm, &mut rx_for_wire, &mut i), + failed_pcm_give, + None, + ) + .context("failed to build output stream")?, + cpal::SampleFormat::I32 => cpal_output + .build_output_stream( + &cpal_output_config.into(), + move |pcm, _: &_| give_output_pcm::(pcm, &mut rx_for_wire, &mut i), + failed_pcm_give, + None, + ) + .context("failed to build output stream")?, + cpal::SampleFormat::F32 => cpal_output + .build_output_stream( + &cpal_output_config.into(), + move |pcm, _: &_| give_output_pcm::(pcm, &mut rx_for_wire, &mut i), + failed_pcm_give, + None, + ) + .context("failed to build output stream")?, + cpal::SampleFormat::F64 => cpal_output + .build_output_stream( + &cpal_output_config.into(), + move |pcm, _: &_| give_output_pcm::(pcm, &mut rx_for_wire, &mut i), + failed_pcm_give, + None, + ) + .context("failed to build output stream")?, + cpal::SampleFormat::U64 => cpal_output + .build_output_stream( + &cpal_output_config.into(), + move |pcm, _: &_| give_output_pcm::(pcm, &mut rx_for_wire, &mut i), + failed_pcm_give, + None, + ) + .context("failed to build output stream")?, + cpal::SampleFormat::I64 => cpal_output + .build_output_stream( + &cpal_output_config.into(), + move |pcm, _: &_| give_output_pcm::(pcm, &mut rx_for_wire, &mut i), + failed_pcm_give, + None, + ) + .context("failed to build output stream")?, + sample_format => Err(anyhow::Error::msg(format!( + "unsupported sample format '{}'! please ask developers to add it", + sample_format + )))?, + }; + + output_stream + .play() + .context("failed to start ingive audio stream")?; + + loop{} + + Ok(()) +} + +fn give_output_pcm(pcm: &mut [T], channel: &mut Receiver, i: &mut f64) +where + T: Sample + std::fmt::Debug + FromSample + FromSample, +{ + println!("wanted {} samples", pcm.len()); + let mut signal = dasp_signal::rate(384000.0).const_hz(100.0).sine(); + for outgoing in pcm.iter_mut() { + *outgoing = match channel.try_recv() { + Ok(sample) => sample.to_sample::(), + //Err(_) => (std::f64::consts::PI * 2.0*(*i)/(384000.0/100.0)).sin().to_sample::(), //T::EQUILIBRIUM, + Err(_) => { + signal.next().to_sample::() + } + }; + *i = *i % 384000.0 + 1.0; + //if *i > 200000.0 { break; } + } + //let mut ctr = 0usize; +} diff --git a/src/dsp_inb/mod.rs b/src/dsp_inb/mod.rs index e18f392..0c12e98 100644 --- a/src/dsp_inb/mod.rs +++ b/src/dsp_inb/mod.rs @@ -4,10 +4,10 @@ use tracing::{error, info, trace, warn}; use tokio::sync::mpsc::{Receiver, UnboundedSender}; -use crate::task_msg::{RawIpDataMessage, PcmFrameMessage}; +use crate::task_msg::{RawEthFrameMessage, PcmFrameMessage}; pub async fn dsp_inb_main( - tx_for_ip_inb: UnboundedSender, + tx_for_eth_inb: UnboundedSender, rx_for_demod: Receiver, ) -> Result<()> { trace!("pcm_inb task started"); diff --git a/src/main.rs b/src/main.rs index dca4f8a..389876d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,15 +14,21 @@ use tokio::sync::mpsc::{channel, unbounded_channel}; //use ringbuf:: -mod task_msg; -use crate::task_msg::{PcmFrameMessage, RawIpDataMessage}; - mod audio_receiver; use crate::audio_receiver::audio_receiver_main; +mod audio_transmitter; +use crate::audio_transmitter::audio_transmitter_main; + mod dsp_inb; use crate::dsp_inb::dsp_inb_main; +mod tap_junction; +use crate::tap_junction::tap_junction_main; + +mod task_msg; +use crate::task_msg::{PcmFrameMessage, PcmSampleMessage, RawEthFrameMessage}; + mod wire_fmt; //#[derive(argh::FromArgs)] @@ -39,14 +45,14 @@ async fn main() -> Result<()> { "no audio input could be found; is a microphone installed?" )?; let cpal_output = cpal_host.default_output_device().context( - "no audio output could be found; is a sound card installed?", + "no audio output could be found; is a sound card installed?" )?; let (tx_for_demod, rx_for_demod) = channel::(1024); - let (tx_for_ip_inb, rx_for_ip_inb) = - unbounded_channel::(); - let (tx_for_mod, rx_for_mod) = unbounded_channel::(); - let (tx_for_wire, rx_for_wire) = unbounded_channel::(); + let (tx_for_eth_inb, rx_for_eth_inb) = + unbounded_channel::(); + let (tx_for_mod, rx_for_mod) = channel::(1024); + let (tx_for_wire, rx_for_wire) = channel::(1); trace!("starting listener for audio on wire"); audio_receiver_main(tx_for_demod, cpal_input) @@ -56,20 +62,19 @@ async fn main() -> Result<()> { let task_dsp_inb = tasks.spawn(async move { trace!("starting inbound audio demodulator"); - dsp_inb_main(tx_for_ip_inb, rx_for_demod).await + dsp_inb_main(tx_for_eth_inb, rx_for_demod).await + }); + let task_tap_junction = tasks.spawn(async move { + trace!("Starting tap manager"); + tap_junction_main(tx_for_mod, rx_for_eth_inb).await }); - //let task_tun_junction = tasks.spawn(async move { - // trace!("Starting tun manager"); - // tun_junction_main(tx_for_mod, rx_for_ip_inb).await - //}); //let task_dsp_outb = tasks.spawn(async move { // trace!("Starting outbound audio modulator"); // dsp_outb_main(tx_for_wire, rx_for_mod).await //}); - //let task_audio_sender = tasks.spawn(async move { - // trace!("Starting audio transmitter"); - // audio_sender_main(rx_for_mod, cpal_output).await - //}); + trace!("Starting audio sender"); + audio_transmitter_main(rx_for_wire, cpal_output) + .context("transmitter for audio on wire has failed to start")?; while let Some(task_result) = tasks.join_next().await { task_result?? diff --git a/src/tap_junction/mod.rs b/src/tap_junction/mod.rs new file mode 100644 index 0000000..ef4bd2c --- /dev/null +++ b/src/tap_junction/mod.rs @@ -0,0 +1,25 @@ +use anyhow::{Context, Result}; + +use crate::task_msg::RawEthFrameMessage; + +pub async fn tap_junction_main( + tx_for_mod: tokio::sync::mpsc::Sender, + rx_for_eth_inb: tokio::sync::mpsc::UnboundedReceiver, +) -> Result<()> { + const MTU: i32 = 1500; + let tap_dev = tokio_tun::Tun::builder() + .tap() + .name("sang") + .mtu(MTU) + .address("10.185.0.1".parse()?) + .netmask("255.255.255.0".parse()?) + .up() + .try_build() + .context("failed to construct tap device! could this be a permissions issue? try `setcap CAP_NET_ADMIN=ep /path/to/binary`")?; + + let mut buf = [0u8; MTU as usize]; + loop { + let frame_length = tap_dev.recv(&mut buf).await?; + println!("{:?}", &buf[..frame_length]); + } +} diff --git a/src/task_msg.rs b/src/task_msg.rs index 17b369f..371f539 100644 --- a/src/task_msg.rs +++ b/src/task_msg.rs @@ -1,2 +1,3 @@ pub type PcmFrameMessage = Vec; -pub type RawIpDataMessage = Vec; +pub type PcmSampleMessage = i64; +pub type RawEthFrameMessage = Vec; diff --git a/src/wire_fmt/cap.rs b/src/wire_fmt/cap.rs index 42320c0..84ae9c3 100644 --- a/src/wire_fmt/cap.rs +++ b/src/wire_fmt/cap.rs @@ -3,7 +3,7 @@ pub struct Capabilities { } pub enum Service { - elephone = 1, + Telephone = 1, Typewriter = 2, IpDce = 3, EthernetDce = 4,