]>
code.octet-stream.net Git - m17rt/blob - m17app/src/soundmodem.rs
8b7f80f74aa4765aba9e74bd3667375c87b91c33
   1 use std
::io
::{self, ErrorKind
, Read
, Write
}; 
   3 use crate::tnc
::{Tnc
, TncError
}; 
   5 use m17core
::kiss
::MAX_FRAME_LEN
; 
   6 use m17core
::modem
::{Demodulator
, SoftDemodulator
}; 
   7 use m17core
::tnc
::SoftTnc
; 
   9 use std
::path
::PathBuf
; 
  10 use std
::sync
::mpsc
::{channel
, sync_channel
, Receiver
, Sender
, SyncSender
, TryRecvError
}; 
  11 use std
::sync
::{Arc
, Mutex
}; 
  12 use std
::time
::{Duration
, Instant
}; 
  14 pub struct Soundmodem 
{ 
  15     event_tx
: SyncSender
<SoundmodemEvent
>, 
  16     kiss_out_rx
: Arc
<Mutex
<Receiver
<Arc
<[u8]>>>>, 
  17     partial_kiss_out
: Arc
<Mutex
<Option
<PartialKissOut
>>>, 
  21     pub fn new_with_input
<T
: InputSource
>(input
: T
) -> Self { 
  22         // must create TNC here 
  23         let (event_tx
, event_rx
) = sync_channel(128); 
  24         let (kiss_out_tx
, kiss_out_rx
) = sync_channel(128); 
  25         spawn_soundmodem_worker(event_tx
.clone(), event_rx
, kiss_out_tx
, Box
::new(input
)); 
  28             kiss_out_rx
: Arc
::new(Mutex
::new(kiss_out_rx
)), 
  29             partial_kiss_out
: Arc
::new(Mutex
::new(None
)), 
  34 struct PartialKissOut 
{ 
  39 impl Read 
for Soundmodem 
{ 
  40     fn read(&mut self, buf
: &mut [u8]) -> io
::Result
<usize> { 
  42             let mut partial_kiss_out 
= self.partial_kiss_out
.lock().unwrap
(); 
  43             if let Some(partial
) = partial_kiss_out
.as_mut() { 
  44                 let remaining 
= partial
.output
.len() - partial
.idx
; 
  45                 let to_write 
= remaining
.min(buf
.len()); 
  47                     .copy_from_slice(&partial
.output
[partial
.idx
..(partial
.idx 
+ to_write
)]); 
  48                 if to_write 
== remaining 
{ 
  49                     *partial_kiss_out 
= None
; 
  51                     partial
.idx 
+= to_write
; 
  57             let rx 
= self.kiss_out_rx
.lock().unwrap
(); 
  59                 .map_err(|s
| io
::Error
::new(ErrorKind
::Other
, format
!("{:?}", s
)))?
 
  61         let to_write 
= output
.len().min(buf
.len()); 
  62         buf
[0..to_write
].copy_from_slice(&output
[0..to_write
]); 
  63         if to_write 
!= output
.len() { 
  64             *self.partial_kiss_out
.lock().unwrap
() = Some(PartialKissOut 
{ 
  73 impl Write 
for Soundmodem 
{ 
  74     fn write(&mut self, buf
: &[u8]) -> std
::io
::Result
<usize> { 
  75         let _ 
= self.event_tx
.try_send(SoundmodemEvent
::Kiss(buf
.into
())); 
  79     fn flush(&mut self) -> std
::io
::Result
<()> { 
  84 impl Tnc 
for Soundmodem 
{ 
  85     fn try_clone(&mut self) -> Result
<Self, TncError
> { 
  87             event_tx
: self.event_tx
.clone(), 
  88             kiss_out_rx
: self.kiss_out_rx
.clone(), 
  89             partial_kiss_out
: self.partial_kiss_out
.clone(), 
  93     fn start(&mut self) -> Result
<(), TncError
> { 
  94         let _ 
= self.event_tx
.send(SoundmodemEvent
::Start
); 
  98     fn close(&mut self) -> Result
<(), TncError
> { 
  99         let _ 
= self.event_tx
.send(SoundmodemEvent
::Close
); 
 104 pub enum SoundmodemEvent 
{ 
 106     BasebandInput(Arc
<[i16]>), 
 111 fn spawn_soundmodem_worker( 
 112     event_tx
: SyncSender
<SoundmodemEvent
>, 
 113     event_rx
: Receiver
<SoundmodemEvent
>, 
 114     kiss_out_tx
: SyncSender
<Arc
<[u8]>>, 
 115     input
: Box
<dyn InputSource
>, 
 117     std
::thread
::spawn(move || { 
 118         // TODO: should be able to provide a custom Demodulator for a soundmodem 
 119         let mut demod 
= SoftDemodulator
::new(); 
 120         let mut tnc 
= SoftTnc
::new(); 
 121         let mut buf 
= [0u8; MAX_FRAME_LEN
]; 
 122         while let Ok(ev
) = event_rx
.recv() { 
 124                 SoundmodemEvent
::Kiss(k
) => { 
 125                     let _n 
= tnc
.write_kiss(&k
); 
 126                     // TODO: what does it mean if we fail to write it all? 
 127                     // Probably we have to read frames for tx first - revisit this during tx 
 129                 SoundmodemEvent
::BasebandInput(b
) => { 
 131                         if let Some(frame
) = demod
.demod(*sample
) { 
 132                             tnc
.handle_frame(frame
); 
 134                                 let n 
= tnc
.read_kiss(&mut buf
); 
 136                                     let _ 
= kiss_out_tx
.try_send(buf
[0..n
].into
()); 
 144                 SoundmodemEvent
::Start 
=> input
.start(event_tx
.clone()), 
 145                 SoundmodemEvent
::Close 
=> break, 
 151 pub trait InputSource
: Send 
+ Sync 
+ '
static { 
 152     fn start(&self, samples
: SyncSender
<SoundmodemEvent
>); 
 156 pub struct InputSoundcard 
{ 
 160 impl InputSource 
for InputSoundcard 
{ 
 161     fn start(&self, samples
: SyncSender
<SoundmodemEvent
>) { 
 170 pub struct InputRrcFile 
{ 
 172     end_tx
: Mutex
<Option
<Sender
<()>>>, 
 176     pub fn new(path
: PathBuf
) -> Self { 
 179             end_tx
: Mutex
::new(None
), 
 184 impl InputSource 
for InputRrcFile 
{ 
 185     fn start(&self, samples
: SyncSender
<SoundmodemEvent
>) { 
 186         let (end_tx
, end_rx
) = channel(); 
 187         let path 
= self.path
.clone(); 
 188         std
::thread
::spawn(move || { 
 189             // TODO: error handling 
 190             let mut file 
= File
::open(path
).unwrap
(); 
 191             let mut baseband 
= vec
![]; 
 192             file
.read_to_end(&mut baseband
).unwrap
(); 
 194             // assuming 48 kHz for now 
 195             const TICK
: Duration 
= Duration
::from_millis(25); 
 196             const SAMPLES_PER_TICK
: usize = 1200; 
 198             let mut next_tick 
= Instant
::now() + TICK
; 
 199             let mut buf 
= [0i16; SAMPLES_PER_TICK
]; 
 202             for sample 
in baseband
 
 204                 .map(|pair
| i16::from_le_bytes([pair
[0], pair
[1]])) 
 208                 if idx 
== SAMPLES_PER_TICK 
{ 
 209                     if let Err(e
) = samples
.try_send(SoundmodemEvent
::BasebandInput(buf
.into
())) { 
 210                         debug
!("overflow feeding soundmodem: {e:?}"); 
 212                     next_tick 
= next_tick 
+ TICK
; 
 214                     std
::thread
::sleep(next_tick
.duration_since(Instant
::now())); 
 216                 if end_rx
.try_recv() != Err(TryRecvError
::Empty
) { 
 221         *self.end_tx
.lock().unwrap
() = Some(end_tx
); 
 225         let _ 
= self.end_tx
.lock().unwrap
().take();