From: Thomas Karpiniec Date: Sun, 5 Jan 2025 03:27:40 +0000 (+1100) Subject: Defining input sources for Soundmodem X-Git-Url: https://code.octet-stream.net/m17rt/commitdiff_plain/b0e7a62e7d8184888184ca9d11464611ed12a97c Defining input sources for Soundmodem --- diff --git a/demod/src/main.rs b/demod/src/main.rs index c1a7eb9..78ff2b0 100755 --- a/demod/src/main.rs +++ b/demod/src/main.rs @@ -57,7 +57,12 @@ pub fn run_my_decode() { let stream = def .build_output_stream( &config.into(), - move |data: &mut [i16], _: &cpal::OutputCallbackInfo| { + move |data: &mut [i16], info: &cpal::OutputCallbackInfo| { + debug!( + "callback {:?} playback {:?}", + info.timestamp().callback, + info.timestamp().playback + ); println!( "iteration {counter} asked for {} samples at time {}", data.len(), @@ -72,7 +77,7 @@ pub fn run_my_decode() { data[0..qty].copy_from_slice(&samples[index..(index + qty)]); index += qty; }, - move |e| { + move |_e| { println!("error occurred"); }, None, 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(); + } +} diff --git a/m17codec2/src/lib.rs b/m17codec2/src/lib.rs index eb45f77..f09d2b8 100755 --- a/m17codec2/src/lib.rs +++ b/m17codec2/src/lib.rs @@ -1,9 +1,12 @@ use codec2::{Codec2, Codec2Mode}; - +use cpal::traits::DeviceTrait; +use cpal::traits::HostTrait; +use cpal::traits::StreamTrait; +use cpal::{Sample, SampleFormat, SampleRate}; +use log::debug; use m17app::adapter::StreamAdapter; use m17app::app::TxHandle; use m17core::protocol::LsfFrame; - use std::collections::VecDeque; use std::fs::File; use std::io::Write; @@ -13,13 +16,6 @@ use std::sync::{ Arc, Mutex, }; -use cpal::traits::DeviceTrait; -use cpal::traits::HostTrait; -use cpal::traits::StreamTrait; -use cpal::{Sample, SampleFormat, SampleRate}; - -use log::debug; - pub fn decode_codec2>(data: &[u8], out_path: P) -> Vec { let codec2 = Codec2::new(Codec2Mode::MODE_3200); let var_name = codec2; @@ -142,7 +138,12 @@ fn stream_thread(end: Receiver<()>, state: Arc>, output_card let stream = device .build_output_stream( &config.into(), - move |data: &mut [i16], _: &cpal::OutputCallbackInfo| { + move |data: &mut [i16], info: &cpal::OutputCallbackInfo| { + debug!( + "callback {:?} playback {:?}", + info.timestamp().callback, + info.timestamp().playback + ); output_cb(data, &state); }, |e| {