Files
tap/src/audio_source.rs
T

120 lines
3.6 KiB
Rust

use crate::tx_thread::TxThread;
use crate::udp_connection::OpusUdpConnection;
use crate::util::{SAMPLE_RATE, SAMPLE_RATE_RAW, STEREO_FRAME_SIZE, ThreadMessage};
use anyhow::Error;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::{BufferSize, FromSample, Host, Sample, SampleRate, StreamConfig};
use log::{error, info};
use opus::{Application, Bitrate, Channels, Encoder, Signal};
use std::io::{Read, stdin};
use std::net::SocketAddr;
use std::sync::mpsc::{Sender, channel};
use std::thread;
pub fn tap(
device: &str,
src_addr: SocketAddr,
dest_addr: SocketAddr,
host: Host,
) -> Result<(), Error> {
// Set up the input device and stream with the default input config.
let device = if device == "default" {
host.default_input_device()
} else {
let id = &device.parse().expect("failed to parse input device id");
host.device_by_id(id)
}
.expect("failed to find input device");
for dev in host.input_devices()? {
info!("ID: {}", dev.id()?);
}
info!("Input device: {}", device.id()?);
let config = device
.default_input_config()
.expect("Failed to get default input config");
let mut stream_config = StreamConfig::from(config);
let count = stream_config.channels;
stream_config.sample_rate = SAMPLE_RATE_RAW as SampleRate;
stream_config.buffer_size = BufferSize::Fixed(STEREO_FRAME_SIZE as u32 / count as u32);
for cfg in device.supported_input_configs()? {
info!("{:?}", cfg)
}
info!("Stream config: {stream_config:?}");
let udp_connection = OpusUdpConnection::new(src_addr, dest_addr)?;
let mut encoder = Encoder::new(SAMPLE_RATE, Channels::Stereo, Application::Audio)?;
encoder.set_bitrate(Bitrate::Max)?;
encoder.set_complexity(10)?;
encoder.set_signal(Signal::Music)?;
let (sender, receiver) = channel();
let tx_thread = TxThread::new(receiver, encoder, udp_connection);
thread::spawn(move || tx_thread.thread());
let err_fn = move |err| {
error!("an error occurred on stream: {err}");
};
let stream = match config.sample_format() {
cpal::SampleFormat::I8 => device.build_input_stream(
stream_config,
move |data, _: &_| write_input_data::<i8>(data, &sender),
err_fn,
None,
)?,
cpal::SampleFormat::I16 => device.build_input_stream(
stream_config,
move |data, _: &_| write_input_data::<i16>(data, &sender),
err_fn,
None,
)?,
cpal::SampleFormat::I32 => device.build_input_stream(
stream_config,
move |data, _: &_| write_input_data::<i32>(data, &sender),
err_fn,
None,
)?,
cpal::SampleFormat::F32 => device.build_input_stream(
stream_config,
move |data, _: &_| write_input_data::<f32>(data, &sender),
err_fn,
None,
)?,
sample_format => {
return Err(anyhow::Error::msg(format!(
"Unsupported sample format '{sample_format}'"
)));
}
};
info!("Running tap...");
stream.play()?;
let mut stdin = stdin().lock();
let mut exit = [0u8; 1];
stdin.read_exact(&mut exit)?;
info!("Exiting...");
Ok(())
}
fn write_input_data<T>(input: &[T], ctx: &Sender<ThreadMessage>)
where
T: Sample,
f32: FromSample<T>,
{
let sample: Vec<f32> = input.iter().map(|s| f32::from_sample(*s)).collect();
if let Err(err) = ctx.send(ThreadMessage::Data(sample)) {
error!("Got error when sending data: {err}")
}
}