use anyhow::{Context, Result}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use cpal::{FromSample, Sample, SampleFormat}; use tracing::{error, info, trace, warn}; use tokio::sync::mpsc::Sender; use crate::task_msg::PcmFrameMessage; pub fn audio_receiver_main( tx_for_demod: Sender, cpal_input: &cpal::Device, ) -> Result { trace!("audio_receiver started"); 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, 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::(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 '{}'! please ask developers to add it", sample_format) ))? } }; input_stream.play().context("failed to start intake audio stream")?; Ok(input_stream) } fn take_input_pcm(pcm: &[T], channel: &Sender) where T: Sample + std::fmt::Debug, i64: FromSample { if channel.try_send(pcm.iter().map(|&n| n.to_sample::()).collect()).is_err() { error!("demodulator PCM data queue is full???"); } }