]>
code.octet-stream.net Git - m17rt/blob - m17app/src/soundmodem.rs
974fd45085600b8fa60d1f7cac377e765415426d
   1 use crate::tnc
::{Tnc
, TncError
}; 
   3 use m17core
::kiss
::MAX_FRAME_LEN
; 
   4 use m17core
::modem
::{Demodulator
, Modulator
, ModulatorAction
, SoftDemodulator
, SoftModulator
}; 
   5 use m17core
::tnc
::SoftTnc
; 
   6 use std
::collections
::VecDeque
; 
   8 use std
::io
::{self, ErrorKind
, Read
, Write
}; 
   9 use std
::path
::PathBuf
; 
  10 use std
::sync
::mpsc
::{channel
, sync_channel
, Receiver
, Sender
, SyncSender
, TryRecvError
}; 
  11 use std
::sync
::RwLock
; 
  12 use std
::sync
::{Arc
, Mutex
}; 
  13 use std
::time
::{Duration
, Instant
}; 
  15 pub struct Soundmodem 
{ 
  16     event_tx
: SyncSender
<SoundmodemEvent
>, 
  17     kiss_out_rx
: Arc
<Mutex
<Receiver
<Arc
<[u8]>>>>, 
  18     partial_kiss_out
: Arc
<Mutex
<Option
<PartialKissOut
>>>, 
  22     pub fn new
<I
: InputSource
, O
: OutputSink
, P
: Ptt
>(input
: I
, output
: O
, ptt
: P
) -> Self { 
  23         // must create TNC here 
  24         let (event_tx
, event_rx
) = sync_channel(128); 
  25         let (kiss_out_tx
, kiss_out_rx
) = sync_channel(128); 
  26         spawn_soundmodem_worker( 
  36             kiss_out_rx
: Arc
::new(Mutex
::new(kiss_out_rx
)), 
  37             partial_kiss_out
: Arc
::new(Mutex
::new(None
)), 
  42 struct PartialKissOut 
{ 
  47 impl Read 
for Soundmodem 
{ 
  48     fn read(&mut self, buf
: &mut [u8]) -> io
::Result
<usize> { 
  50             let mut partial_kiss_out 
= self.partial_kiss_out
.lock().unwrap
(); 
  51             if let Some(partial
) = partial_kiss_out
.as_mut() { 
  52                 let remaining 
= partial
.output
.len() - partial
.idx
; 
  53                 let to_write 
= remaining
.min(buf
.len()); 
  55                     .copy_from_slice(&partial
.output
[partial
.idx
..(partial
.idx 
+ to_write
)]); 
  56                 if to_write 
== remaining 
{ 
  57                     *partial_kiss_out 
= None
; 
  59                     partial
.idx 
+= to_write
; 
  65             let rx 
= self.kiss_out_rx
.lock().unwrap
(); 
  67                 .map_err(|s
| io
::Error
::new(ErrorKind
::Other
, format
!("{:?}", s
)))?
 
  69         let to_write 
= output
.len().min(buf
.len()); 
  70         buf
[0..to_write
].copy_from_slice(&output
[0..to_write
]); 
  71         if to_write 
!= output
.len() { 
  72             *self.partial_kiss_out
.lock().unwrap
() = Some(PartialKissOut 
{ 
  81 impl Write 
for Soundmodem 
{ 
  82     fn write(&mut self, buf
: &[u8]) -> std
::io
::Result
<usize> { 
  83         let _ 
= self.event_tx
.try_send(SoundmodemEvent
::Kiss(buf
.into
())); 
  87     fn flush(&mut self) -> std
::io
::Result
<()> { 
  92 impl Tnc 
for Soundmodem 
{ 
  93     fn try_clone(&mut self) -> Result
<Self, TncError
> { 
  95             event_tx
: self.event_tx
.clone(), 
  96             kiss_out_rx
: self.kiss_out_rx
.clone(), 
  97             partial_kiss_out
: self.partial_kiss_out
.clone(), 
 101     fn start(&mut self) -> Result
<(), TncError
> { 
 102         let _ 
= self.event_tx
.send(SoundmodemEvent
::Start
); 
 106     fn close(&mut self) -> Result
<(), TncError
> { 
 107         let _ 
= self.event_tx
.send(SoundmodemEvent
::Close
); 
 112 pub enum SoundmodemEvent 
{ 
 114     BasebandInput(Arc
<[i16]>), 
 117     DidReadFromOutputBuffer 
{ len
: usize, timestamp
: Instant 
}, 
 121 fn spawn_soundmodem_worker( 
 122     event_tx
: SyncSender
<SoundmodemEvent
>, 
 123     event_rx
: Receiver
<SoundmodemEvent
>, 
 124     kiss_out_tx
: SyncSender
<Arc
<[u8]>>, 
 125     input
: Box
<dyn InputSource
>, 
 126     output
: Box
<dyn OutputSink
>, 
 127     mut ptt_driver
: Box
<dyn Ptt
>, 
 129     std
::thread
::spawn(move || { 
 130         // TODO: should be able to provide a custom Demodulator for a soundmodem 
 131         let mut demodulator 
= SoftDemodulator
::new(); 
 132         let mut modulator 
= SoftModulator
::new(); 
 133         let mut tnc 
= SoftTnc
::new(); 
 134         let mut buf 
= [0u8; MAX_FRAME_LEN
]; 
 135         let out_buffer 
= Arc
::new(RwLock
::new(OutputBuffer
::new())); 
 136         let mut out_samples 
= [0i16; 1024]; 
 137         let start 
= Instant
::now(); 
 139         while let Ok(ev
) = event_rx
.recv() { 
 140             // Update clock on TNC before we do anything 
 141             let sample_time 
= (start
.elapsed().as_nanos() / 48000) as u64; 
 142             tnc
.set_now(sample_time
); 
 146                 SoundmodemEvent
::Kiss(k
) => { 
 147                     let _n 
= tnc
.write_kiss(&k
); 
 148                     // TODO: what does it mean if we fail to write it all? 
 149                     // Probably we have to read frames for tx first - revisit this during tx 
 151                 SoundmodemEvent
::BasebandInput(b
) => { 
 153                         if let Some(frame
) = demodulator
.demod(*sample
) { 
 154                             tnc
.handle_frame(frame
); 
 156                                 let n 
= tnc
.read_kiss(&mut buf
); 
 158                                     let _ 
= kiss_out_tx
.try_send(buf
[0..n
].into
()); 
 165                     tnc
.set_data_carrier_detect(demodulator
.data_carrier_detect()); 
 167                 SoundmodemEvent
::Start 
=> { 
 168                     input
.start(event_tx
.clone()); 
 169                     output
.start(event_tx
.clone(), out_buffer
.clone()); 
 171                 SoundmodemEvent
::Close 
=> { 
 172                     ptt_driver
.ptt_off(); 
 175                 SoundmodemEvent
::DidReadFromOutputBuffer 
{ len
, timestamp 
} => { 
 176                     let (occupied
, internal_latency
) = { 
 177                         let out_buffer 
= out_buffer
.read().unwrap
(); 
 178                         (out_buffer
.samples
.len(), out_buffer
.latency
) 
 180                     let internal_latency 
= (internal_latency
.as_secs_f32() * 48000.0) as usize; 
 181                     let dynamic_latency 
= 
 182                         len
.saturating_sub((timestamp
.elapsed().as_secs_f32() * 48000.0) as usize); 
 183                     modulator
.update
_o
utp
ut
_b
uffer
( 
 186                         internal_latency 
+ dynamic_latency
, 
 189                 SoundmodemEvent
::OutputUnderrun 
=> { 
 190                     // TODO: cancel transmission, send empty data frame to host 
 195             let new_ptt 
= tnc
.ptt(); 
 200                     ptt_driver
.ptt_off(); 
 205             // Let the modulator do what it wants 
 206             while let Some(action
) = modulator
.run() { 
 208                     ModulatorAction
::SetIdle(idling
) => { 
 209                         out_buffer
.write().unwrap
().idl
ing 
= idling
; 
 211                     ModulatorAction
::GetNextFrame 
=> { 
 212                         modulator
.provide_next_frame(tnc
.read_tx_frame()); 
 214                     ModulatorAction
::ReadOutput 
=> loop { 
 215                         let n 
= modulator
.read_output_samples(&mut out_samples
); 
 219                         let mut out_buffer 
= out_buffer
.write().unwrap
(); 
 220                         for s 
in &out_samples
[0..n
] { 
 221                             out_buffer
.samples
.push_back(*s
); 
 224                     ModulatorAction
::TransmissionWillEnd(in_samples
) => { 
 225                         tnc
.set_tx_end_time(in_samples
); 
 233 pub trait InputSource
: Send 
+ Sync 
+ '
static { 
 234     fn start(&self, samples
: SyncSender
<SoundmodemEvent
>); 
 238 pub struct InputRrcFile 
{ 
 240     end_tx
: Mutex
<Option
<Sender
<()>>>, 
 244     pub fn new(path
: PathBuf
) -> Self { 
 247             end_tx
: Mutex
::new(None
), 
 252 impl InputSource 
for InputRrcFile 
{ 
 253     fn start(&self, samples
: SyncSender
<SoundmodemEvent
>) { 
 254         let (end_tx
, end_rx
) = channel(); 
 255         let path 
= self.path
.clone(); 
 256         std
::thread
::spawn(move || { 
 257             // TODO: error handling 
 258             let mut file 
= File
::open(path
).unwrap
(); 
 259             let mut baseband 
= vec
![]; 
 260             file
.read_to_end(&mut baseband
).unwrap
(); 
 262             // assuming 48 kHz for now 
 263             const TICK
: Duration 
= Duration
::from_millis(25); 
 264             const SAMPLES_PER_TICK
: usize = 1200; 
 266             let mut next_tick 
= Instant
::now() + TICK
; 
 267             let mut buf 
= [0i16; SAMPLES_PER_TICK
]; 
 270             for sample 
in baseband
 
 272                 .map(|pair
| i16::from_le_bytes([pair
[0], pair
[1]])) 
 276                 if idx 
== SAMPLES_PER_TICK 
{ 
 277                     if let Err(e
) = samples
.try_send(SoundmodemEvent
::BasebandInput(buf
.into
())) { 
 278                         debug
!("overflow feeding soundmodem: {e:?}"); 
 282                     std
::thread
::sleep(next_tick
.duration_since(Instant
::now())); 
 284                 if end_rx
.try_recv() != Err(TryRecvError
::Empty
) { 
 289         *self.end_tx
.lock().unwrap
() = Some(end_tx
); 
 293         let _ 
= self.end_tx
.lock().unwrap
().take(); 
 297 pub struct NullInputSource 
{ 
 298     end_tx
: Mutex
<Option
<Sender
<()>>>, 
 301 impl NullInputSource 
{ 
 302     pub fn new() -> Self { 
 304             end_tx
: Mutex
::new(None
), 
 309 impl InputSource 
for NullInputSource 
{ 
 310     fn start(&self, samples
: SyncSender
<SoundmodemEvent
>) { 
 311         let (end_tx
, end_rx
) = channel(); 
 312         std
::thread
::spawn(move || { 
 313             // assuming 48 kHz for now 
 314             const TICK
: Duration 
= Duration
::from_millis(25); 
 315             const SAMPLES_PER_TICK
: usize = 1200; 
 316             let mut next_tick 
= Instant
::now() + TICK
; 
 319                 std
::thread
::sleep(next_tick
.duration_since(Instant
::now())); 
 321                 if end_rx
.try_recv() != Err(TryRecvError
::Empty
) { 
 324                 if let Err(e
) = samples
.try_send(SoundmodemEvent
::BasebandInput( 
 325                     [0i16; SAMPLES_PER_TICK
].into
(), 
 327                     debug
!("overflow feeding soundmodem: {e:?}"); 
 331         *self.end_tx
.lock().unwrap
() = Some(end_tx
); 
 335         let _ 
= self.end_tx
.lock().unwrap
().take(); 
 339 impl Default 
for NullInputSource 
{ 
 340     fn default() -> Self { 
 345 pub struct OutputBuffer 
{ 
 347     // TODO: something more efficient 
 348     pub samples
: VecDeque
<i16>, 
 349     pub latency
: Duration
, 
 353     pub fn new() -> Self { 
 356             samples
: VecDeque
::new(), 
 357             latency
: Duration
::ZERO
, 
 362 impl Default 
for OutputBuffer 
{ 
 363     fn default() -> Self { 
 368 pub trait OutputSink
: Send 
+ Sync 
+ '
static { 
 369     fn start(&self, event_tx
: SyncSender
<SoundmodemEvent
>, buffer
: Arc
<RwLock
<OutputBuffer
>>); 
 373 pub struct OutputRrcFile 
{ 
 375     end_tx
: Mutex
<Option
<Sender
<()>>>, 
 379     pub fn new(path
: PathBuf
) -> Self { 
 382             end_tx
: Mutex
::new(None
), 
 387 impl OutputSink 
for OutputRrcFile 
{ 
 388     fn start(&self, event_tx
: SyncSender
<SoundmodemEvent
>, buffer
: Arc
<RwLock
<OutputBuffer
>>) { 
 389         let (end_tx
, end_rx
) = channel(); 
 390         let path 
= self.path
.clone(); 
 391         std
::thread
::spawn(move || { 
 392             // TODO: error handling 
 393             let mut file 
= File
::create(path
).unwrap
(); 
 395             // assuming 48 kHz for now 
 396             const TICK
: Duration 
= Duration
::from_millis(25); 
 397             const SAMPLES_PER_TICK
: usize = 1200; 
 399             // flattened BE i16s for writing 
 400             let mut buf 
= [0u8; SAMPLES_PER_TICK 
* 2]; 
 401             let mut next_tick 
= Instant
::now() + TICK
; 
 404                 std
::thread
::sleep(next_tick
.duration_since(Instant
::now())); 
 406                 if end_rx
.try_recv() != Err(TryRecvError
::Empty
) { 
 409                 // For now only write deliberately modulated (non-idling) samples 
 410                 // Multiple transmissions will get smooshed together 
 411                 let mut buf_used 
= 0; 
 413                 let mut buffer 
= buffer
.write().unwrap
(); 
 414                 for out 
in buf
.chunks_mut(2) { 
 415                     if let Some(s
) = buffer
.samples
.pop_front() { 
 416                         let be 
= s
.to_le_bytes(); 
 417                         out
.copy_from_slice(&[be
[0], be
[1]]); 
 419                     } else if !buffer
.idl
ing 
{ 
 420                         debug
!("output rrc file had underrun"); 
 421                         let _ 
= event_tx
.send(SoundmodemEvent
::OutputUnderrun
); 
 425                 if let Err(e
) = file
.write_all(&buf
[0..buf_used
]) { 
 426                     debug
!("failed to write to rrc file: {e:?}"); 
 429                 let _ 
= event_tx
.send(SoundmodemEvent
::DidReadFromOutputBuffer 
{ 
 431                     timestamp
: Instant
::now(), 
 435         *self.end_tx
.lock().unwrap
() = Some(end_tx
); 
 439         let _ 
= self.end_tx
.lock().unwrap
().take(); 
 443 pub struct NullOutputSink 
{ 
 444     end_tx
: Mutex
<Option
<Sender
<()>>>, 
 447 impl NullOutputSink 
{ 
 448     pub fn new() -> Self { 
 450             end_tx
: Mutex
::new(None
), 
 455 impl Default 
for NullOutputSink 
{ 
 456     fn default() -> Self { 
 461 impl OutputSink 
for NullOutputSink 
{ 
 462     fn start(&self, event_tx
: SyncSender
<SoundmodemEvent
>, buffer
: Arc
<RwLock
<OutputBuffer
>>) { 
 463         let (end_tx
, end_rx
) = channel(); 
 464         std
::thread
::spawn(move || { 
 465             // assuming 48 kHz for now 
 466             const TICK
: Duration 
= Duration
::from_millis(25); 
 467             const SAMPLES_PER_TICK
: usize = 1200; 
 468             let mut next_tick 
= Instant
::now() + TICK
; 
 471                 std
::thread
::sleep(next_tick
.duration_since(Instant
::now())); 
 473                 if end_rx
.try_recv() != Err(TryRecvError
::Empty
) { 
 477                 let mut buffer 
= buffer
.write().unwrap
(); 
 479                 for _ 
in 0..SAMPLES_PER_TICK 
{ 
 480                     if buffer
.samples
.pop_front().is
_none
() { 
 482                             debug
!("null output had underrun"); 
 483                             let _ 
= event_tx
.send(SoundmodemEvent
::OutputUnderrun
); 
 490                 let _ 
= event_tx
.send(SoundmodemEvent
::DidReadFromOutputBuffer 
{ 
 492                     timestamp
: Instant
::now(), 
 496         *self.end_tx
.lock().unwrap
() = Some(end_tx
); 
 500         let _ 
= self.end_tx
.lock().unwrap
().take(); 
 504 pub trait Ptt
: Send 
+ '
static { 
 505     fn ptt_on(&mut self); 
 506     fn ptt_off(&mut self); 
 509 /// There is no PTT because this TNC will never make transmissions on a real radio. 
 513     pub fn new() -> Self { 
 518 impl Default 
for NullPtt 
{ 
 519     fn default() -> Self { 
 524 impl Ptt 
for NullPtt 
{ 
 525     fn ptt_on(&mut self) {} 
 526     fn ptt_off(&mut self) {}