sang/src/audio_receiver/mod.rs

112 lines
4.4 KiB
Rust

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<PcmFrameMessage>,
cpal_input: &cpal::Device,
) -> Result<cpal::Stream> {
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::<u8>(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::<i8>(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::<i16>(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::<i32>(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::<f32>(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::<f64>(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::<u64>(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::<i64>(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<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???");
}
}