use crate::tx_thread::TxThread; use crate::util::{SAMPLE_RATE, SAMPLE_RATE_RAW, STEREO_FRAME_SIZE, ThreadMessage}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use cpal::{BufferSize, FromSample, Host, Sample, SampleRate, Stream, StreamConfig}; use log::{debug, error, info}; use opus::{Application, Bitrate, Channels, Encoder, Signal}; use std::io::{Read, Write}; use std::sync::mpsc::{Receiver, Sender, channel}; use std::thread; use thiserror::Error; #[derive(Debug, Error)] pub enum Error { #[error("CPAL error: {0}")] CpalError(#[from] cpal::Error), #[error("Opus error: {0}")] OpusError(#[from] opus::Error), #[error("Unknown sample error format")] UnknownSampleErrorFormat, #[error("Unknown device")] UnknownDevice, } #[derive(Debug)] pub enum Commands { SetDevice(String), Pause, Resume, } pub struct AudioSourceThread { device: String, host: Host, cmd_channel: Receiver, } impl AudioSourceThread { pub fn new(device: String, host: Host, cmd_channel: Receiver) -> Self { Self { device, host, cmd_channel, } } fn setup_stream(&mut self, audio_sender: &Sender) -> Result { let device = if self.device == "default" { self.host.default_input_device() } else { let id = &self.device.parse()?; self.host.device_by_id(id) }; let device = device.ok_or(Error::UnknownDevice)?; 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); info!("Stream config: {stream_config:?}"); for cfg in device.supported_input_configs()? { debug!("{:?}", cfg) } let callback_sender = audio_sender.clone(); let stream = match config.sample_format() { cpal::SampleFormat::I8 => device.build_input_stream( stream_config, move |data, _: &_| write_input_data::(data, &callback_sender), err_fn, None, )?, cpal::SampleFormat::I16 => device.build_input_stream( stream_config, move |data, _: &_| write_input_data::(data, &callback_sender), err_fn, None, )?, cpal::SampleFormat::I32 => device.build_input_stream( stream_config, move |data, _: &_| write_input_data::(data, &callback_sender), err_fn, None, )?, cpal::SampleFormat::F32 => device.build_input_stream( stream_config, move |data, _: &_| write_input_data::(data, &callback_sender), err_fn, None, )?, sample_format => { error!("Unknown sample format: '{sample_format}'"); return Err(Error::UnknownSampleErrorFormat); } }; Ok(stream) } pub fn worker(mut self, connection: T) -> Result<(), Error> { let encoder = Self::setup_encoder()?; let (sender, receiver) = channel(); let tx_thread = TxThread::new(receiver, encoder, connection); let thread = thread::spawn(move || tx_thread.thread()); let mut stream = self.setup_stream(&sender)?; info!("Running..."); stream.play()?; loop { let msg = self.cmd_channel.recv(); if let Ok(msg) = msg { match msg { Commands::SetDevice(dev) => { info!("Setting device to {dev}..."); self.device = dev; drop(stream); stream = self.setup_stream(&sender)?; stream.play()?; } Commands::Pause => { info!("Pausing Stream..."); stream.pause()?; } Commands::Resume => { info!("Playing stream..."); stream.play()?; } } } else { error!("Channel closing, stopping threads"); drop(stream); drop(sender); thread.join().unwrap(); return Ok(()); } } } fn setup_encoder() -> Result { 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)?; Ok(encoder) } } fn err_fn(error: cpal::Error) { error!("Got an error processing audio data: {error}") } fn write_input_data(input: &[T], ctx: &Sender) where T: Sample, f32: FromSample, { let sample: Vec = 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}") } }