]> code.octet-stream.net Git - m17rt/blob - m17app/src/soundmodem.rs
Defining input sources for Soundmodem
[m17rt] / m17app / src / soundmodem.rs
1 use std::io::{self, ErrorKind, Read, Write};
2
3 use crate::tnc::{Tnc, TncError};
4 use log::debug;
5 use m17core::tnc::SoftTnc;
6 use std::fs::File;
7 use std::path::PathBuf;
8 use std::sync::mpsc::{channel, sync_channel, Receiver, Sender, SyncSender, TryRecvError};
9 use std::sync::{Arc, Mutex};
10 use std::time::{Duration, Instant};
11
12 pub struct Soundmodem {
13 tnc: SoftTnc,
14 config: SoundmodemConfig,
15 }
16
17 pub struct SoundmodemConfig {
18 // sound cards, PTT, etc.
19 input: Box<dyn InputSource>,
20 }
21
22 impl Read for Soundmodem {
23 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
24 self.tnc
25 .read_kiss(buf)
26 .map_err(|s| io::Error::new(ErrorKind::Other, format!("{:?}", s)))
27 }
28 }
29
30 impl Write for Soundmodem {
31 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
32 self.tnc
33 .write_kiss(buf)
34 .map_err(|s| io::Error::new(ErrorKind::Other, format!("{:?}", s)))
35 }
36
37 fn flush(&mut self) -> std::io::Result<()> {
38 Ok(())
39 }
40 }
41
42 impl Tnc for Soundmodem {
43 fn try_clone(&mut self) -> Result<Self, TncError> {
44 unimplemented!();
45 }
46
47 fn start(&mut self) -> Result<(), TncError> {
48 unimplemented!();
49 }
50
51 fn close(&mut self) -> Result<(), TncError> {
52 unimplemented!();
53 }
54 }
55
56 pub enum SoundmodemEvent {
57 Kiss(Arc<[u8]>),
58 BasebandInput(Arc<[i16]>),
59 }
60
61 pub trait InputSource: Send + Sync + 'static {
62 fn start(&self, samples: SyncSender<SoundmodemEvent>);
63 fn close(&self);
64 }
65
66 pub struct InputSoundcard {
67 cpal_name: String,
68 }
69
70 impl InputSource for InputSoundcard {
71 fn start(&self, samples: SyncSender<SoundmodemEvent>) {
72 todo!()
73 }
74
75 fn close(&self) {
76 todo!()
77 }
78 }
79
80 pub struct InputRrcFile {
81 path: PathBuf,
82 end_tx: Mutex<Option<Sender<()>>>,
83 }
84
85 impl InputSource for InputRrcFile {
86 fn start(&self, samples: SyncSender<SoundmodemEvent>) {
87 let (end_tx, end_rx) = channel();
88 let path = self.path.clone();
89 std::thread::spawn(move || {
90 // TODO: error handling
91 let mut file = File::open(path).unwrap();
92 let mut baseband = vec![];
93 file.read_to_end(&mut baseband).unwrap();
94
95 // assuming 48 kHz for now
96 const TICK: Duration = Duration::from_millis(25);
97 const SAMPLES_PER_TICK: usize = 1200;
98
99 let mut next_tick = Instant::now() + TICK;
100 let mut buf = [0i16; SAMPLES_PER_TICK];
101 let mut idx = 0;
102
103 for sample in baseband
104 .chunks(2)
105 .map(|pair| i16::from_le_bytes([pair[0], pair[1]]))
106 {
107 buf[idx] = sample;
108 idx += 1;
109 if idx == SAMPLES_PER_TICK {
110 if let Err(e) = samples.try_send(SoundmodemEvent::BasebandInput(buf.into())) {
111 debug!("overflow feeding soundmodem: {e:?}");
112 }
113 next_tick = next_tick + TICK;
114 idx = 0;
115 std::thread::sleep(next_tick.duration_since(Instant::now()));
116 }
117 if end_rx.try_recv() != Err(TryRecvError::Empty) {
118 break;
119 }
120 }
121 });
122 *self.end_tx.lock().unwrap() = Some(end_tx);
123 }
124
125 fn close(&self) {
126 let _ = self.end_tx.lock().unwrap().take();
127 }
128 }