carrier now uses live sample rate

This commit is contained in:
TerraMaster85 2024-12-20 22:44:24 -05:00
parent bb283c6e47
commit b092907a8a
7 changed files with 104 additions and 78 deletions

View file

@ -105,8 +105,9 @@ pub fn audio_receiver_main(
Ok(input_stream) Ok(input_stream)
} }
fn take_input_pcm<T>(pcm: &[T], channel: &Sender<PcmFrameMessage>) where T: Sample + std::fmt::Debug, i64: FromSample<T> { fn take_input_pcm<T>(pcm: &[T], channel: &Sender<PcmFrameMessage>) where T: Sample + std::fmt::Debug, f64: FromSample<T> {
if channel.try_send(pcm.iter().map(|&n| n.to_sample::<i64>()).collect()).is_err() { let _ = pcm;
error!("demodulator PCM data queue is full???"); //if channel.try_send(pcm.iter().map(|&n| n.to_sample::<f64>()).collect()).is_err() {
} // error!("demodulator PCM data queue is full???");
//}
} }

View file

@ -7,10 +7,12 @@ use tracing::{error, info, trace, warn};
use tokio::sync::mpsc::Receiver; use tokio::sync::mpsc::Receiver;
use crate::task_msg::{PcmFrameMessage, PcmSampleMessage};
use crate::task_msg::{PcmFrameMessage, PcmSampleMessage, SampleRateNotifyMessage};
pub fn audio_transmitter_main( pub fn audio_transmitter_main(
mut rx_for_wire: Receiver<PcmSampleMessage>, mut rx_for_wire: Receiver<PcmSampleMessage>,
mut tx_sample_rate_notify: tokio::sync::oneshot::Sender<SampleRateNotifyMessage>,
cpal_output: &cpal::Device, cpal_output: &cpal::Device,
) -> Result<cpal::Stream> { ) -> Result<cpal::Stream> {
trace!("audio_transmitter started"); trace!("audio_transmitter started");
@ -33,6 +35,7 @@ pub fn audio_transmitter_main(
} }
}).expect("output device has no supported configurations available") }).expect("output device has no supported configurations available")
.with_max_sample_rate(); .with_max_sample_rate();
//.with_sample_rate(cpal::SampleRate(44100));
info!( info!(
"output samples are {} @ {}hz on {} channels", "output samples are {} @ {}hz on {} channels",
@ -48,7 +51,7 @@ pub fn audio_transmitter_main(
let output_stream = match cpal_output_config.sample_format() { let output_stream = match cpal_output_config.sample_format() {
cpal::SampleFormat::U8 => cpal_output cpal::SampleFormat::U8 => cpal_output
.build_output_stream( .build_output_stream(
&cpal_output_config.into(), &cpal_output_config.clone().into(),
move |pcm, _: &_| give_output_pcm::<u8>(pcm, &mut rx_for_wire), move |pcm, _: &_| give_output_pcm::<u8>(pcm, &mut rx_for_wire),
failed_pcm_give, failed_pcm_give,
None, None,
@ -56,7 +59,7 @@ pub fn audio_transmitter_main(
.context("failed to build output stream")?, .context("failed to build output stream")?,
cpal::SampleFormat::I8 => cpal_output cpal::SampleFormat::I8 => cpal_output
.build_output_stream( .build_output_stream(
&cpal_output_config.into(), &cpal_output_config.clone().into(),
move |pcm, _: &_| give_output_pcm::<i8>(pcm, &mut rx_for_wire), move |pcm, _: &_| give_output_pcm::<i8>(pcm, &mut rx_for_wire),
failed_pcm_give, failed_pcm_give,
None, None,
@ -64,7 +67,7 @@ pub fn audio_transmitter_main(
.context("failed to build output stream")?, .context("failed to build output stream")?,
cpal::SampleFormat::U16 => cpal_output cpal::SampleFormat::U16 => cpal_output
.build_output_stream( .build_output_stream(
&cpal_output_config.into(), &cpal_output_config.clone().into(),
move |pcm, _: &_| give_output_pcm::<u16>(pcm, &mut rx_for_wire), move |pcm, _: &_| give_output_pcm::<u16>(pcm, &mut rx_for_wire),
failed_pcm_give, failed_pcm_give,
None, None,
@ -72,7 +75,7 @@ pub fn audio_transmitter_main(
.context("failed to build output stream")?, .context("failed to build output stream")?,
cpal::SampleFormat::I16 => cpal_output cpal::SampleFormat::I16 => cpal_output
.build_output_stream( .build_output_stream(
&cpal_output_config.into(), &cpal_output_config.clone().into(),
move |pcm, _: &_| give_output_pcm::<i16>(pcm, &mut rx_for_wire), move |pcm, _: &_| give_output_pcm::<i16>(pcm, &mut rx_for_wire),
failed_pcm_give, failed_pcm_give,
None, None,
@ -80,7 +83,7 @@ pub fn audio_transmitter_main(
.context("failed to build output stream")?, .context("failed to build output stream")?,
cpal::SampleFormat::U32 => cpal_output cpal::SampleFormat::U32 => cpal_output
.build_output_stream( .build_output_stream(
&cpal_output_config.into(), &cpal_output_config.clone().into(),
move |pcm, _: &_| give_output_pcm::<u32>(pcm, &mut rx_for_wire), move |pcm, _: &_| give_output_pcm::<u32>(pcm, &mut rx_for_wire),
failed_pcm_give, failed_pcm_give,
None, None,
@ -88,7 +91,7 @@ pub fn audio_transmitter_main(
.context("failed to build output stream")?, .context("failed to build output stream")?,
cpal::SampleFormat::I32 => cpal_output cpal::SampleFormat::I32 => cpal_output
.build_output_stream( .build_output_stream(
&cpal_output_config.into(), &cpal_output_config.clone().into(),
move |pcm, _: &_| give_output_pcm::<i32>(pcm, &mut rx_for_wire), move |pcm, _: &_| give_output_pcm::<i32>(pcm, &mut rx_for_wire),
failed_pcm_give, failed_pcm_give,
None, None,
@ -96,7 +99,7 @@ pub fn audio_transmitter_main(
.context("failed to build output stream")?, .context("failed to build output stream")?,
cpal::SampleFormat::F32 => cpal_output cpal::SampleFormat::F32 => cpal_output
.build_output_stream( .build_output_stream(
&cpal_output_config.into(), &cpal_output_config.clone().into(),
move |pcm, _: &_| give_output_pcm::<f32>(pcm, &mut rx_for_wire), move |pcm, _: &_| give_output_pcm::<f32>(pcm, &mut rx_for_wire),
failed_pcm_give, failed_pcm_give,
None, None,
@ -104,7 +107,7 @@ pub fn audio_transmitter_main(
.context("failed to build output stream")?, .context("failed to build output stream")?,
cpal::SampleFormat::F64 => cpal_output cpal::SampleFormat::F64 => cpal_output
.build_output_stream( .build_output_stream(
&cpal_output_config.into(), &cpal_output_config.clone().into(),
move |pcm, _: &_| give_output_pcm::<f64>(pcm, &mut rx_for_wire), move |pcm, _: &_| give_output_pcm::<f64>(pcm, &mut rx_for_wire),
failed_pcm_give, failed_pcm_give,
None, None,
@ -112,7 +115,7 @@ pub fn audio_transmitter_main(
.context("failed to build output stream")?, .context("failed to build output stream")?,
cpal::SampleFormat::U64 => cpal_output cpal::SampleFormat::U64 => cpal_output
.build_output_stream( .build_output_stream(
&cpal_output_config.into(), &cpal_output_config.clone().into(),
move |pcm, _: &_| give_output_pcm::<u64>(pcm, &mut rx_for_wire), move |pcm, _: &_| give_output_pcm::<u64>(pcm, &mut rx_for_wire),
failed_pcm_give, failed_pcm_give,
None, None,
@ -120,7 +123,7 @@ pub fn audio_transmitter_main(
.context("failed to build output stream")?, .context("failed to build output stream")?,
cpal::SampleFormat::I64 => cpal_output cpal::SampleFormat::I64 => cpal_output
.build_output_stream( .build_output_stream(
&cpal_output_config.into(), &cpal_output_config.clone().into(),
move |pcm, _: &_| give_output_pcm::<i64>(pcm, &mut rx_for_wire), move |pcm, _: &_| give_output_pcm::<i64>(pcm, &mut rx_for_wire),
failed_pcm_give, failed_pcm_give,
None, None,
@ -132,6 +135,10 @@ pub fn audio_transmitter_main(
)))?, )))?,
}; };
// Inform dsp_outb about sample rate
tx_sample_rate_notify.send(cpal_output_config.sample_rate().0)
.expect("dsp_outb has dropped srn rx; it has probably panicked");
output_stream output_stream
.play() .play()
.context("failed to start output audio stream")?; .context("failed to start output audio stream")?;
@ -141,15 +148,19 @@ pub fn audio_transmitter_main(
fn give_output_pcm<T>(pcm: &mut [T], channel: &mut Receiver<PcmSampleMessage>) fn give_output_pcm<T>(pcm: &mut [T], channel: &mut Receiver<PcmSampleMessage>)
where where
T: Sample + std::fmt::Debug + FromSample<i64> + FromSample<f64>, T: Sample + std::fmt::Debug + FromSample<f64>,
{ {
trace!("ALSA demands {} samples...", pcm.len()); trace!("ALSA demands {} samples...", pcm.len());
let mut have_complained_no_messages = false;
for outgoing in pcm.iter_mut() { for outgoing in pcm.iter_mut() {
*outgoing = match channel.try_recv() { *outgoing = match channel.try_recv() {
Ok(sample) => sample.to_sample::<T>(), Ok(sample) => sample.to_sample::<T>(),
Err(_) => { Err(_) => {
warn!("rx_for_wire has no samples for us! carrier dead!"); //if have_complained_no_messages == true {
warn!("are we able to produce samples quickly enough? CPU OK?"); warn!("rx_for_wire has no samples for us! carrier dead!");
warn!("are we able to produce samples quickly enough? CPU OK?");
//have_complained_no_messages = false;
//}
T::EQUILIBRIUM T::EQUILIBRIUM
} }
}; };

View file

@ -7,6 +7,7 @@ const TAU: f64 = 2.0 * std::f64::consts::PI;
pub struct TwoStateCarrier { pub struct TwoStateCarrier {
pub sample_rate: f64, pub sample_rate: f64,
pub symbol_rate: f64,
pub freq_low: f64, pub freq_low: f64,
pub freq_high: f64, pub freq_high: f64,
phase_low: f64, phase_low: f64,
@ -15,7 +16,7 @@ pub struct TwoStateCarrier {
pub trait Carrier { pub trait Carrier {
fn byte_into_fsk_samples(&mut self, byte: &u8) -> Vec<PcmSample>; fn byte_into_fsk_samples(&mut self, byte: &u8) -> Vec<PcmSample>;
fn idle(&mut self) -> [PcmSample; 8]; fn idle(&mut self) -> [PcmSample; 64];
} }
impl Carrier for TwoStateCarrier { impl Carrier for TwoStateCarrier {
@ -37,32 +38,32 @@ impl Carrier for TwoStateCarrier {
// for each bit in the byte (LSb..=MSb)... // for each bit in the byte (LSb..=MSb)...
for bit_place in 1..=8 { for bit_place in 1..=8 {
// if the bit is high... // if the bit is high...
if (byte >> bit_place) & 1 == 0 { if (byte >> (bit_place - 1)) & 1 == 0 {
// while the signal hasn't yet completed a single period... // while the signal hasn't yet completed a single period...
while (self.phase_low) < self.sample_rate / self.freq_low { while (self.phase_low) < self.sample_rate / self.symbol_rate {
// push the next sample of the relative sine wave // push the next sample of the relative sine wave
out.push(PcmSample::F64( out.push(
(self.phase_low * TAU * self.freq_low (self.phase_low * TAU * self.freq_low
/ self.sample_rate) / self.sample_rate)
.sin(), .sin()
)); );
// next sample // next sample
self.phase_low += 1.0; self.phase_low += 1.0;
} }
// if phase counter exceeds 1 period, drop it down again // if phase counter exceeds 1 period, drop it down again
self.phase_low = self.phase_low =
self.phase_low % (self.sample_rate / self.freq_low); self.phase_low % (self.sample_rate / self.symbol_rate);
} else if (byte >> bit_place) & 1 == 1 { } else if (byte >> (bit_place - 1)) & 1 == 1 {
while (self.phase_high) < self.sample_rate / self.freq_high { while (self.phase_high) < self.sample_rate / self.symbol_rate {
out.push(PcmSample::F64( out.push(
(self.phase_high * TAU * self.freq_high (self.phase_high * TAU * self.freq_high
/ self.sample_rate) / self.sample_rate)
.sin(), .sin(),
)); );
self.phase_high += 1.0; self.phase_high += 1.0;
} }
self.phase_high = self.phase_high =
self.phase_high % (self.sample_rate / self.freq_high); self.phase_high % (self.sample_rate / self.symbol_rate);
} else { } else {
// this keeps happening // this keeps happening
unreachable!("FSK bit math returned a value that isn't 0 or 1"); unreachable!("FSK bit math returned a value that isn't 0 or 1");
@ -72,13 +73,11 @@ impl Carrier for TwoStateCarrier {
out out
} }
fn idle(&mut self) -> [PcmSample; 8] { fn idle(&mut self) -> [PcmSample; 64] {
const INIT_VALUE: PcmSample = PcmSample::F64(0.0); let mut out: [PcmSample; 64] = [0.0; 64];
let mut out: [PcmSample; 8] = [INIT_VALUE; 8]; for i in 0..=63 {
for i in 0..=7 { out[i] =
out[i] = PcmSample::F64( (self.phase_low * TAU * self.freq_low/ self.sample_rate).sin();
(self.phase_low * TAU * self.freq_low/ self.sample_rate).sin()
);
self.phase_low += 1.0; self.phase_low += 1.0;
} }
self.phase_low = self.phase_low % (self.sample_rate / self.freq_low); self.phase_low = self.phase_low % (self.sample_rate / self.freq_low);
@ -87,11 +86,12 @@ impl Carrier for TwoStateCarrier {
} }
impl TwoStateCarrier { impl TwoStateCarrier {
pub fn new(freq_low: u32, freq_high: u32, sample_rate: u32) -> Self { pub fn new(freq_low: u32, freq_high: u32, sample_rate: u32, symbol_rate: u32) -> Self {
TwoStateCarrier { TwoStateCarrier {
freq_low: freq_low.into(), freq_low: freq_low.into(),
freq_high: freq_high.into(), freq_high: freq_high.into(),
sample_rate: sample_rate.into(), sample_rate: sample_rate.into(),
symbol_rate: symbol_rate.into(),
phase_low: 0_f64, phase_low: 0_f64,
phase_high: 0_f64, phase_high: 0_f64,
} }

View file

@ -1,11 +1,12 @@
#[derive(Copy)] //#[derive(Copy)]
pub enum PcmSample { //pub enum PcmSample {
U8(u8), // U8(u8),
I8(i8), // I8(i8),
U16(u16), // U16(u16),
I32(i32), // I32(i32),
F32(f32), // F32(f32),
F64(f64), // F64(f64),
U64(u64), // U64(u64),
I64(i64), // I64(i64),
} //}
pub type PcmSample = f64;

View file

@ -7,30 +7,49 @@ use tracing::{error, info, trace, warn};
use tokio::sync::mpsc::{Receiver, Sender}; use tokio::sync::mpsc::{Receiver, Sender};
use tokio::sync::mpsc::error::TryRecvError; use tokio::sync::mpsc::error::TryRecvError;
use crate::task_msg::{RawEthFrameMessage, PcmSampleMessage}; use crate::task_msg::{RawEthFrameMessage, PcmSampleMessage, SampleRateNotifyMessage};
use crate::dsp_common::carrier::{Carrier, TwoStateCarrier}; use crate::dsp_common::carrier::{Carrier, TwoStateCarrier};
pub async fn dsp_outb_main( pub async fn dsp_outb_main(
tx_for_wire: Sender<PcmSampleMessage>, tx_for_wire: Sender<PcmSampleMessage>,
mut rx_for_mod: Receiver<RawEthFrameMessage>, mut rx_for_mod: Receiver<RawEthFrameMessage>,
mut rx_sample_rate_notify: tokio::sync::oneshot::Receiver<SampleRateNotifyMessage>,
) -> Result<()> { ) -> Result<()> {
trace!("pcm_outb task started"); trace!("pcm_outb task started");
let carrier_phase = 0_f64;
let carrier_low = 240;
let carrier_high = 250;
// TODO: fetch real sample rate // TODO: fetch real sample rate
let mut carrier = TwoStateCarrier::new(carrier_low, carrier_high, 384000); let mut carrier = TwoStateCarrier::new(
320, // freq_low
384, // freq_high
rx_sample_rate_notify.await?, // sample_rate
4, // symbol_rate
);
loop { loop {
let eth_frame = match rx_for_mod.try_recv() { match rx_for_mod.try_recv() {
Ok(x) => x, Ok(eth_frame) => {
trace!("network frame came in");
let mut txed = 0;
let mut bytes = 0;
for byte in eth_frame {
for samp in carrier.byte_into_fsk_samples(&byte) {
tx_for_wire.send(samp).await?;
txed += 1;
}
bytes += 1;
}
trace!("eth frame modulated and sent ({} bytes, {} samps)", bytes, txed);
//(*carrier_phase * TAU * freq / sample_rate).sin()
},
Err(TryRecvError::Empty) => { Err(TryRecvError::Empty) => {
trace!("no eth frames to modulate!"); trace!("no eth frames to modulate!");
//tx_for_wire.send(i64::EQUILIBRIUM).await?; //tx_for_wire.send(i64::EQUILIBRIUM).await?;
//continue; //continue;
vec![0, 0, 0, 0, 0, 0, 0, 0,] for samp in carrier.idle() {
tx_for_wire.send(samp).await?;
}
} }
Err(TryRecvError::Disconnected) => { Err(TryRecvError::Disconnected) => {
Err(anyhow::Error::msg( Err(anyhow::Error::msg(
@ -38,19 +57,5 @@ pub async fn dsp_outb_main(
))? ))?
} }
}; };
trace!("network frame came in");
let mut txed = 0;
let mut bytes = 0;
for byte in eth_frame {
for samp in carrier.byte_into_fsk_samples(&byte) {
tx_for_wire.send(samp).await?;
txed += 1;
}
bytes += 1;
}
trace!("eth frame modulated and sent ({} bytes, {} samps)", bytes, txed);
//(*carrier_phase * TAU * freq / sample_rate).sin()
} }
} }

View file

@ -10,6 +10,8 @@ use cpal::traits::HostTrait;
use tracing::{trace}; use tracing::{trace};
use tracing_subscriber::layer::SubscriberExt;
use tokio::sync::mpsc::{channel, unbounded_channel}; use tokio::sync::mpsc::{channel, unbounded_channel};
//use ringbuf:: //use ringbuf::
@ -32,7 +34,7 @@ mod tap_junction;
use crate::tap_junction::tap_junction_main; use crate::tap_junction::tap_junction_main;
mod task_msg; mod task_msg;
use crate::task_msg::{PcmFrameMessage, PcmSampleMessage, RawEthFrameMessage}; use crate::task_msg::{PcmFrameMessage, PcmSampleMessage, RawEthFrameMessage, SampleRateNotifyMessage};
mod wire_fmt; mod wire_fmt;
@ -57,7 +59,10 @@ async fn main() -> Result<()> {
let (tx_for_eth_inb, rx_for_eth_inb) = let (tx_for_eth_inb, rx_for_eth_inb) =
unbounded_channel::<RawEthFrameMessage>(); unbounded_channel::<RawEthFrameMessage>();
let (tx_for_mod, rx_for_mod) = channel::<RawEthFrameMessage>(1024); let (tx_for_mod, rx_for_mod) = channel::<RawEthFrameMessage>(1024);
let (tx_for_wire, rx_for_wire) = channel::<PcmSampleMessage>(1024); let (tx_for_wire, rx_for_wire) = channel::<PcmSampleMessage>(256);
let (tx_sample_rate_notify, rx_sample_rate_notify) =
tokio::sync::oneshot::channel::<SampleRateNotifyMessage>();
trace!("starting listener for audio on wire"); trace!("starting listener for audio on wire");
let audio_in_stream = audio_receiver_main(tx_for_demod, &cpal_input) let audio_in_stream = audio_receiver_main(tx_for_demod, &cpal_input)
@ -75,10 +80,10 @@ async fn main() -> Result<()> {
}); });
let task_dsp_outb = tasks.spawn(async move { let task_dsp_outb = tasks.spawn(async move {
trace!("Starting outbound audio modulator"); trace!("Starting outbound audio modulator");
dsp_outb_main(tx_for_wire, rx_for_mod).await dsp_outb_main(tx_for_wire, rx_for_mod, rx_sample_rate_notify).await
}); });
trace!("Starting audio sender"); trace!("Starting audio sender");
let audio_out_stream = audio_transmitter_main(rx_for_wire, &cpal_output) let audio_out_stream = audio_transmitter_main(rx_for_wire, tx_sample_rate_notify, &cpal_output)
.context("transmitter for audio on wire has failed to start")?; .context("transmitter for audio on wire has failed to start")?;
while let Some(task_result) = tasks.join_next().await { while let Some(task_result) = tasks.join_next().await {

View file

@ -1,3 +1,6 @@
pub type PcmFrameMessage = Vec<crate::dsp_common::sample::PcmSample>; use crate::dsp_common::sample::PcmSample;
pub type PcmSampleMessage = i64;
pub type PcmFrameMessage = Vec<PcmSample>;
pub type PcmSampleMessage = PcmSample;
pub type RawEthFrameMessage = Vec<u8>; pub type RawEthFrameMessage = Vec<u8>;
pub type SampleRateNotifyMessage = u32;