X-Git-Url: https://code.octet-stream.net/m17rt/blobdiff_plain/2813fcb83452c7fd91d799a984613a616180a067..b0e7a62e7d8184888184ca9d11464611ed12a97c:/m17app/src/soundmodem.rs?ds=sidebyside diff --git a/m17app/src/soundmodem.rs b/m17app/src/soundmodem.rs index 83731da..b6fbf46 100644 --- a/m17app/src/soundmodem.rs +++ b/m17app/src/soundmodem.rs @@ -1,7 +1,13 @@ use std::io::{self, ErrorKind, Read, Write}; use crate::tnc::{Tnc, TncError}; +use log::debug; use m17core::tnc::SoftTnc; +use std::fs::File; +use std::path::PathBuf; +use std::sync::mpsc::{channel, sync_channel, Receiver, Sender, SyncSender, TryRecvError}; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; pub struct Soundmodem { tnc: SoftTnc, @@ -10,6 +16,7 @@ pub struct Soundmodem { pub struct SoundmodemConfig { // sound cards, PTT, etc. + input: Box, } impl Read for Soundmodem { @@ -45,3 +52,77 @@ impl Tnc for Soundmodem { unimplemented!(); } } + +pub enum SoundmodemEvent { + Kiss(Arc<[u8]>), + BasebandInput(Arc<[i16]>), +} + +pub trait InputSource: Send + Sync + 'static { + fn start(&self, samples: SyncSender); + fn close(&self); +} + +pub struct InputSoundcard { + cpal_name: String, +} + +impl InputSource for InputSoundcard { + fn start(&self, samples: SyncSender) { + todo!() + } + + fn close(&self) { + todo!() + } +} + +pub struct InputRrcFile { + path: PathBuf, + end_tx: Mutex>>, +} + +impl InputSource for InputRrcFile { + fn start(&self, samples: SyncSender) { + let (end_tx, end_rx) = channel(); + let path = self.path.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; + + let mut next_tick = Instant::now() + TICK; + let mut buf = [0i16; SAMPLES_PER_TICK]; + let mut idx = 0; + + for sample in baseband + .chunks(2) + .map(|pair| i16::from_le_bytes([pair[0], pair[1]])) + { + buf[idx] = sample; + idx += 1; + if idx == SAMPLES_PER_TICK { + if let Err(e) = samples.try_send(SoundmodemEvent::BasebandInput(buf.into())) { + debug!("overflow feeding soundmodem: {e:?}"); + } + next_tick = next_tick + TICK; + idx = 0; + std::thread::sleep(next_tick.duration_since(Instant::now())); + } + if end_rx.try_recv() != Err(TryRecvError::Empty) { + break; + } + } + }); + *self.end_tx.lock().unwrap() = Some(end_tx); + } + + fn close(&self) { + let _ = self.end_tx.lock().unwrap().take(); + } +}