X-Git-Url: https://code.octet-stream.net/m17rt/blobdiff_plain/6440cd74346c4b2d63d4774476e8c6113c032534..2fb25de49daca6ddff6f5af13bcf7c314aafafb3:/m17app/src/soundmodem.rs diff --git a/m17app/src/soundmodem.rs b/m17app/src/soundmodem.rs index 2d005b1..1cb871a 100644 --- a/m17app/src/soundmodem.rs +++ b/m17app/src/soundmodem.rs @@ -1,3 +1,4 @@ +use crate::error::{M17Error, SoundmodemError}; use crate::tnc::{Tnc, TncError}; use log::debug; use m17core::kiss::MAX_FRAME_LEN; @@ -138,8 +139,12 @@ fn spawn_soundmodem_worker( let mut ptt = false; while let Ok(ev) = event_rx.recv() { // Update clock on TNC before we do anything - let sample_time = (start.elapsed().as_nanos() / 48000) as u64; - tnc.set_now(sample_time); + let sample_time = start.elapsed(); + let secs = sample_time.as_secs(); + let nanos = sample_time.subsec_nanos(); + // Accurate to within approx 1 sample + let now_samples = 48000 * secs + (nanos as u64 / 20833); + tnc.set_now(now_samples); // Handle event match ev { @@ -165,11 +170,12 @@ fn spawn_soundmodem_worker( tnc.set_data_carrier_detect(demodulator.data_carrier_detect()); } SoundmodemEvent::Start => { - input.start(event_tx.clone()); - output.start(event_tx.clone(), out_buffer.clone()); + // TODO: runtime event handling + input.start(event_tx.clone()).unwrap(); + output.start(event_tx.clone(), out_buffer.clone()).unwrap(); } SoundmodemEvent::Close => { - ptt_driver.ptt_off(); + ptt_driver.ptt_off().unwrap(); break; } SoundmodemEvent::DidReadFromOutputBuffer { len, timestamp } => { @@ -195,9 +201,9 @@ fn spawn_soundmodem_worker( let new_ptt = tnc.ptt(); if new_ptt != ptt { if new_ptt { - ptt_driver.ptt_on(); + ptt_driver.ptt_on().unwrap(); } else { - ptt_driver.ptt_off(); + ptt_driver.ptt_off().unwrap(); } } ptt = new_ptt; @@ -231,34 +237,33 @@ fn spawn_soundmodem_worker( } pub trait InputSource: Send + Sync + 'static { - fn start(&self, samples: SyncSender); - fn close(&self); + fn start(&self, samples: SyncSender) -> Result<(), SoundmodemError>; + fn close(&self) -> Result<(), SoundmodemError>; } pub struct InputRrcFile { - path: PathBuf, + baseband: Arc<[u8]>, end_tx: Mutex>>, } impl InputRrcFile { - pub fn new(path: PathBuf) -> Self { - Self { - path, + pub fn new(path: PathBuf) -> Result { + let mut file = File::open(&path).map_err(|_| M17Error::InvalidRrcPath(path.clone()))?; + let mut baseband = vec![]; + file.read_to_end(&mut baseband) + .map_err(|_| M17Error::RrcReadFailed(path))?; + Ok(Self { + baseband: baseband.into(), end_tx: Mutex::new(None), - } + }) } } impl InputSource for InputRrcFile { - fn start(&self, samples: SyncSender) { + fn start(&self, samples: SyncSender) -> Result<(), SoundmodemError> { let (end_tx, end_rx) = channel(); - let path = self.path.clone(); + let baseband = self.baseband.clone(); std::thread::spawn(move || { - // TODO: error handling - let mut file = File::open(path).unwrap(); - let mut baseband = vec![]; - file.read_to_end(&mut baseband).unwrap(); - // assuming 48 kHz for now const TICK: Duration = Duration::from_millis(25); const SAMPLES_PER_TICK: usize = 1200; @@ -277,7 +282,7 @@ impl InputSource for InputRrcFile { if let Err(e) = samples.try_send(SoundmodemEvent::BasebandInput(buf.into())) { debug!("overflow feeding soundmodem: {e:?}"); } - next_tick = next_tick + TICK; + next_tick += TICK; idx = 0; std::thread::sleep(next_tick.duration_since(Instant::now())); } @@ -287,10 +292,12 @@ impl InputSource for InputRrcFile { } }); *self.end_tx.lock().unwrap() = Some(end_tx); + Ok(()) } - fn close(&self) { + fn close(&self) -> Result<(), SoundmodemError> { let _ = self.end_tx.lock().unwrap().take(); + Ok(()) } } @@ -307,7 +314,7 @@ impl NullInputSource { } impl InputSource for NullInputSource { - fn start(&self, samples: SyncSender) { + fn start(&self, samples: SyncSender) -> Result<(), SoundmodemError> { let (end_tx, end_rx) = channel(); std::thread::spawn(move || { // assuming 48 kHz for now @@ -317,7 +324,7 @@ impl InputSource for NullInputSource { loop { std::thread::sleep(next_tick.duration_since(Instant::now())); - next_tick = next_tick + TICK; + next_tick += TICK; if end_rx.try_recv() != Err(TryRecvError::Empty) { break; } @@ -329,10 +336,18 @@ impl InputSource for NullInputSource { } }); *self.end_tx.lock().unwrap() = Some(end_tx); + Ok(()) } - fn close(&self) { + fn close(&self) -> Result<(), SoundmodemError> { let _ = self.end_tx.lock().unwrap().take(); + Ok(()) + } +} + +impl Default for NullInputSource { + fn default() -> Self { + Self::new() } } @@ -353,9 +368,19 @@ impl OutputBuffer { } } +impl Default for OutputBuffer { + fn default() -> Self { + Self::new() + } +} + pub trait OutputSink: Send + Sync + 'static { - fn start(&self, event_tx: SyncSender, buffer: Arc>); - fn close(&self); + fn start( + &self, + event_tx: SyncSender, + buffer: Arc>, + ) -> Result<(), SoundmodemError>; + fn close(&self) -> Result<(), SoundmodemError>; } pub struct OutputRrcFile { @@ -373,13 +398,14 @@ impl OutputRrcFile { } impl OutputSink for OutputRrcFile { - fn start(&self, event_tx: SyncSender, buffer: Arc>) { + fn start( + &self, + event_tx: SyncSender, + buffer: Arc>, + ) -> Result<(), SoundmodemError> { let (end_tx, end_rx) = channel(); - let path = self.path.clone(); + let mut file = File::create(self.path.clone())?; std::thread::spawn(move || { - // TODO: error handling - let mut file = File::create(path).unwrap(); - // assuming 48 kHz for now const TICK: Duration = Duration::from_millis(25); const SAMPLES_PER_TICK: usize = 1200; @@ -390,7 +416,7 @@ impl OutputSink for OutputRrcFile { loop { std::thread::sleep(next_tick.duration_since(Instant::now())); - next_tick = next_tick + TICK; + next_tick += TICK; if end_rx.try_recv() != Err(TryRecvError::Empty) { break; } @@ -421,10 +447,12 @@ impl OutputSink for OutputRrcFile { } }); *self.end_tx.lock().unwrap() = Some(end_tx); + Ok(()) } - fn close(&self) { + fn close(&self) -> Result<(), SoundmodemError> { let _ = self.end_tx.lock().unwrap().take(); + Ok(()) } } @@ -440,8 +468,18 @@ impl NullOutputSink { } } +impl Default for NullOutputSink { + fn default() -> Self { + Self::new() + } +} + impl OutputSink for NullOutputSink { - fn start(&self, event_tx: SyncSender, buffer: Arc>) { + fn start( + &self, + event_tx: SyncSender, + buffer: Arc>, + ) -> Result<(), SoundmodemError> { let (end_tx, end_rx) = channel(); std::thread::spawn(move || { // assuming 48 kHz for now @@ -451,7 +489,7 @@ impl OutputSink for NullOutputSink { loop { std::thread::sleep(next_tick.duration_since(Instant::now())); - next_tick = next_tick + TICK; + next_tick += TICK; if end_rx.try_recv() != Err(TryRecvError::Empty) { break; } @@ -459,7 +497,7 @@ impl OutputSink for NullOutputSink { let mut buffer = buffer.write().unwrap(); let mut taken = 0; for _ in 0..SAMPLES_PER_TICK { - if !buffer.samples.pop_front().is_some() { + if buffer.samples.pop_front().is_none() { if !buffer.idling { debug!("null output had underrun"); let _ = event_tx.send(SoundmodemEvent::OutputUnderrun); @@ -476,16 +514,18 @@ impl OutputSink for NullOutputSink { } }); *self.end_tx.lock().unwrap() = Some(end_tx); + Ok(()) } - fn close(&self) { + fn close(&self) -> Result<(), SoundmodemError> { let _ = self.end_tx.lock().unwrap().take(); + Ok(()) } } pub trait Ptt: Send + 'static { - fn ptt_on(&mut self); - fn ptt_off(&mut self); + fn ptt_on(&mut self) -> Result<(), SoundmodemError>; + fn ptt_off(&mut self) -> Result<(), SoundmodemError>; } /// There is no PTT because this TNC will never make transmissions on a real radio. @@ -497,7 +537,18 @@ impl NullPtt { } } +impl Default for NullPtt { + fn default() -> Self { + Self::new() + } +} + impl Ptt for NullPtt { - fn ptt_on(&mut self) {} - fn ptt_off(&mut self) {} + fn ptt_on(&mut self) -> Result<(), SoundmodemError> { + Ok(()) + } + + fn ptt_off(&mut self) -> Result<(), SoundmodemError> { + Ok(()) + } }