beginnings of prog architecture
This commit is contained in:
commit
c47228e4d9
9 changed files with 1492 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
2
.rustfmt.toml
Normal file
2
.rustfmt.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
max_width = 80
|
||||
edition = "2024"
|
1265
Cargo.lock
generated
Normal file
1265
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "sang"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.94"
|
||||
cpal = "0.15.3"
|
||||
log = "0.4.22"
|
||||
ringbuf = "0.4.7"
|
||||
simple_logger = { version = "^5.0.0", features = ["colors", "threads", "timestamps"] }
|
||||
thiserror = "2.0.6"
|
||||
tokio = { version = '^1', features = ["full"] }
|
110
src/audio_receiver/mod.rs
Normal file
110
src/audio_receiver/mod.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
use anyhow::{Context, Result};
|
||||
|
||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
use cpal::{FromSample, Sample, SampleFormat};
|
||||
|
||||
use log::{error, info, trace, warn};
|
||||
|
||||
use tokio::sync::mpsc::Sender;
|
||||
|
||||
use crate::msg::PcmFrameMessage;
|
||||
|
||||
pub fn audio_receiver_main(
|
||||
tx_for_demod: Sender<PcmFrameMessage>,
|
||||
cpal_input: cpal::Device,
|
||||
) -> Result<()> {
|
||||
trace!("audio_receiver started");
|
||||
let cpal_input_config = cpal_input.supported_input_configs()?.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("input device has no supported configurations available")
|
||||
.with_max_sample_rate();
|
||||
|
||||
info!(
|
||||
"input samples are {} @ {}hz",
|
||||
&cpal_input_config.sample_format(),
|
||||
&cpal_input_config.sample_rate().0,
|
||||
);
|
||||
|
||||
let failed_pcm_take = move |e| {
|
||||
warn!("dropped some PCM data on input stream?! {}", e);
|
||||
};
|
||||
|
||||
let input_stream = match cpal_input_config.sample_format() {
|
||||
cpal::SampleFormat::U8 => cpal_input.build_input_stream(
|
||||
&cpal_input_config.into(),
|
||||
move |pcm, _: &_| take_input_pcm::<u8>(pcm, &tx_for_demod),
|
||||
failed_pcm_take,
|
||||
None,
|
||||
)?,
|
||||
cpal::SampleFormat::I8 => cpal_input.build_input_stream(
|
||||
&cpal_input_config.into(),
|
||||
move |pcm, _: &_| take_input_pcm::<i8>(pcm, &tx_for_demod),
|
||||
failed_pcm_take,
|
||||
None,
|
||||
)?,
|
||||
cpal::SampleFormat::I16 => cpal_input.build_input_stream(
|
||||
&cpal_input_config.into(),
|
||||
move |pcm, _: &_| take_input_pcm::<i16>(pcm, &tx_for_demod),
|
||||
failed_pcm_take,
|
||||
None,
|
||||
)?,
|
||||
cpal::SampleFormat::I32 => cpal_input.build_input_stream(
|
||||
&cpal_input_config.into(),
|
||||
move |pcm, _: &_| take_input_pcm::<i32>(pcm, &tx_for_demod),
|
||||
failed_pcm_take,
|
||||
None,
|
||||
)?,
|
||||
cpal::SampleFormat::F32 => cpal_input.build_input_stream(
|
||||
&cpal_input_config.into(),
|
||||
move |pcm, _: &_| take_input_pcm::<f32>(pcm, &tx_for_demod),
|
||||
failed_pcm_take,
|
||||
None,
|
||||
)?,
|
||||
cpal::SampleFormat::F64 => cpal_input.build_input_stream(
|
||||
&cpal_input_config.into(),
|
||||
move |pcm, _: &_| take_input_pcm::<f64>(pcm, &tx_for_demod),
|
||||
failed_pcm_take,
|
||||
None,
|
||||
)?,
|
||||
cpal::SampleFormat::U64 => cpal_input.build_input_stream(
|
||||
&cpal_input_config.into(),
|
||||
move |pcm, _: &_| take_input_pcm::<u64>(pcm, &tx_for_demod),
|
||||
failed_pcm_take,
|
||||
None,
|
||||
)?,
|
||||
cpal::SampleFormat::I64 => cpal_input.build_input_stream(
|
||||
&cpal_input_config.into(),
|
||||
move |pcm, _: &_| take_input_pcm::<i64>(pcm, &tx_for_demod),
|
||||
failed_pcm_take,
|
||||
None,
|
||||
)?,
|
||||
sample_format => {
|
||||
Err(anyhow::Error::msg(
|
||||
format!(
|
||||
"unsupported sample format '{}'", sample_format)
|
||||
))?
|
||||
}
|
||||
};
|
||||
|
||||
input_stream.play()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn take_input_pcm<T>(pcm: &[T], channel: &Sender<PcmFrameMessage>) where T: Sample + std::fmt::Debug, i64: FromSample<T> {
|
||||
if channel.try_send(pcm.iter().map(|&n| n.to_sample::<i64>()).collect()).is_err() {
|
||||
error!("demodulator PCM data queue is full???");
|
||||
}
|
||||
}
|
16
src/dsp_inb/mod.rs
Normal file
16
src/dsp_inb/mod.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use anyhow::{Context, Result};
|
||||
|
||||
use log::{error, info, trace, warn};
|
||||
|
||||
use tokio::sync::mpsc::{Receiver, UnboundedSender};
|
||||
|
||||
use crate::msg::{RawIpDataMessage, PcmFrameMessage};
|
||||
|
||||
pub async fn dsp_inb_main(
|
||||
tx_for_ip_inb: UnboundedSender<RawIpDataMessage>,
|
||||
rx_for_demod: Receiver<PcmFrameMessage>,
|
||||
) -> Result<()> {
|
||||
trace!("pcm_inb task started");
|
||||
|
||||
Ok(())
|
||||
}
|
76
src/main.rs
Normal file
76
src/main.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
#![cfg_attr(
|
||||
debug_assertions,
|
||||
allow(dead_code, unused_imports, unused_variables)
|
||||
)]
|
||||
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use cpal::traits::HostTrait;
|
||||
|
||||
use log::{trace};
|
||||
|
||||
use tokio::sync::mpsc::{channel, unbounded_channel};
|
||||
|
||||
//use ringbuf::
|
||||
|
||||
mod msg;
|
||||
use crate::msg::{PcmFrameMessage, RawIpDataMessage};
|
||||
|
||||
mod audio_receiver;
|
||||
use crate::audio_receiver::audio_receiver_main;
|
||||
|
||||
mod dsp_inb;
|
||||
use crate::dsp_inb::dsp_inb_main;
|
||||
|
||||
//#[derive(argh::FromArgs)]
|
||||
//struct Cli {}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
simple_logger::init_with_level(log::Level::Trace)
|
||||
.expect("logger init failure???");
|
||||
|
||||
let cpal_host = cpal::default_host();
|
||||
let cpal_input = cpal_host.default_input_device().context(
|
||||
"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?",
|
||||
)?;
|
||||
|
||||
let (tx_for_demod, rx_for_demod) = channel::<PcmFrameMessage>(1024);
|
||||
let (tx_for_ip_inb, rx_for_ip_inb) =
|
||||
unbounded_channel::<RawIpDataMessage>();
|
||||
let (tx_for_mod, rx_for_mod) = unbounded_channel::<RawIpDataMessage>();
|
||||
let (tx_for_wire, rx_for_wire) = unbounded_channel::<PcmFrameMessage>();
|
||||
|
||||
trace!("starting listener for audio on wire");
|
||||
audio_receiver_main(tx_for_demod, cpal_input)
|
||||
.context("listener for audio on wire has failed to start");
|
||||
|
||||
let mut tasks = tokio::task::JoinSet::new();
|
||||
|
||||
let task_dsp_inb = tasks.spawn(async move {
|
||||
trace!("starting inbound audio demodulator");
|
||||
dsp_inb_main(tx_for_ip_inb, rx_for_demod).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
|
||||
//});
|
||||
|
||||
while let Some(task_result) = tasks.join_next().await {
|
||||
task_result??
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
2
src/msg.rs
Normal file
2
src/msg.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub type PcmFrameMessage = Vec<i64>;
|
||||
pub type RawIpDataMessage = Vec<u8>;
|
7
src/wire-fmt/mod.rs
Normal file
7
src/wire-fmt/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
pub enum SpecialCapability {
|
||||
pub StereoOut = 1,
|
||||
pub StereoIn = 2,
|
||||
pub Telephone = 3,
|
||||
pub Typewriter = 4,
|
||||
pub IpDce = 5,
|
||||
}
|
Loading…
Reference in a new issue