+ Start,
+ Close,
+ DidReadFromOutputBuffer {
+ len: usize,
+ timestamp: Instant,
+ },
+ OutputUnderrun,
+}
+
+fn spawn_soundmodem_worker(
+ event_tx: SyncSender<SoundmodemEvent>,
+ event_rx: Receiver<SoundmodemEvent>,
+ kiss_out_tx: SyncSender<Arc<[u8]>>,
+ input: Box<dyn InputSource>,
+ output: Box<dyn OutputSink>,
+) {
+ std::thread::spawn(move || {
+ // TODO: should be able to provide a custom Demodulator for a soundmodem
+ let mut demodulator = SoftDemodulator::new();
+ let mut modulator = SoftModulator::new();
+ let mut tnc = SoftTnc::new();
+ let mut buf = [0u8; MAX_FRAME_LEN];
+ let out_buffer = Arc::new(RwLock::new(OutputBuffer::new()));
+ let mut out_samples = [0i16; 1024];
+ let start = Instant::now();
+ 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);
+
+ // Handle event
+ match ev {
+ SoundmodemEvent::Kiss(k) => {
+ let _n = tnc.write_kiss(&k);
+ // TODO: what does it mean if we fail to write it all?
+ // Probably we have to read frames for tx first - revisit this during tx
+ }
+ SoundmodemEvent::BasebandInput(b) => {
+ for sample in &*b {
+ if let Some(frame) = demodulator.demod(*sample) {
+ tnc.handle_frame(frame);
+ loop {
+ let n = tnc.read_kiss(&mut buf);
+ if n > 0 {
+ let _ = kiss_out_tx.try_send(buf[0..n].into());
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ tnc.set_data_carrier_detect(demodulator.data_carrier_detect());
+ }
+ SoundmodemEvent::Start => {
+ input.start(event_tx.clone());
+ output.start(event_tx.clone(), out_buffer.clone());
+ },
+ SoundmodemEvent::Close => break,
+ SoundmodemEvent::DidReadFromOutputBuffer { len, timestamp } => {
+ let (occupied, internal_latency) = {
+ let out_buffer = out_buffer.read().unwrap();
+ (out_buffer.samples.len(), out_buffer.latency)
+ };
+ let internal_latency = (internal_latency.as_secs_f32() * 48000.0) as usize;
+ let dynamic_latency = len.saturating_sub((timestamp.elapsed().as_secs_f32() * 48000.0) as usize);
+ modulator.update_output_buffer(occupied, 48000, internal_latency + dynamic_latency);
+ },
+ SoundmodemEvent::OutputUnderrun => {
+ // TODO: cancel transmission, send empty data frame to host
+ }
+ }
+
+ // Let the modulator do what it wants
+ while let Some(action) = modulator.run() {
+ match action {
+ ModulatorAction::SetIdle(idling) => {
+ out_buffer.write().unwrap().idling = idling;
+ },
+ ModulatorAction::GetNextFrame => {
+ modulator.provide_next_frame(tnc.read_tx_frame());
+ },
+ ModulatorAction::ReadOutput => {
+ loop {
+ let n = modulator.read_output_samples(&mut out_samples);
+ if n == 0 {
+ break;
+ }
+ let mut out_buffer = out_buffer.write().unwrap();
+ for s in &out_samples[0..n] {
+ out_buffer.samples.push_back(*s);
+ }
+ }
+
+ },
+ ModulatorAction::TransmissionWillEnd(in_samples) => {
+ tnc.set_tx_end_time(in_samples);
+ },
+ }
+ }
+ }
+ });