]>
code.octet-stream.net Git - m17rt/blob - m17app/src/soundmodem.rs
e7c0022643e52047c703943e9d1597eea787cb79
1 use crate::error
::{M17Error
, SoundmodemError
};
2 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
};
16 pub struct Soundmodem
{
17 event_tx
: SyncSender
<SoundmodemEvent
>,
18 kiss_out_rx
: Arc
<Mutex
<Receiver
<Arc
<[u8]>>>>,
19 partial_kiss_out
: Arc
<Mutex
<Option
<PartialKissOut
>>>,
20 error_handler
: ErrorHandlerInternal
,
24 pub fn new
<I
: InputSource
, O
: OutputSink
, P
: Ptt
, E
: ErrorHandler
>(
30 let (event_tx
, event_rx
) = sync_channel(128);
31 let (kiss_out_tx
, kiss_out_rx
) = sync_channel(128);
32 let runtime_error_handler
: ErrorHandlerInternal
= Arc
::new(Mutex
::new(Box
::new(error
)));
33 spawn_soundmodem_worker(
40 runtime_error_handler
.clone(),
44 kiss_out_rx
: Arc
::new(Mutex
::new(kiss_out_rx
)),
45 partial_kiss_out
: Arc
::new(Mutex
::new(None
)),
46 error_handler
: runtime_error_handler
,
51 #[derive(Debug, Clone, Copy)]
52 pub enum ErrorSource
{
58 pub trait ErrorHandler
: Send
+ Sync
+ '
static {
59 fn soundmodem_error(&mut self, source
: ErrorSource
, err
: SoundmodemError
);
62 impl<F
> ErrorHandler
for F
64 F
: FnMut(ErrorSource
, SoundmodemError
) + Send
+ Sync
+ '
static,
66 fn soundmodem_error(&mut self, source
: ErrorSource
, err
: SoundmodemError
) {
71 pub struct NullErrorHandler
;
73 impl NullErrorHandler
{
74 pub fn new() -> Self {
79 impl Default
for NullErrorHandler
{
80 fn default() -> Self {
85 impl ErrorHandler
for NullErrorHandler
{
86 fn soundmodem_error(&mut self, source
: ErrorSource
, err
: SoundmodemError
) {
92 type ErrorHandlerInternal
= Arc
<Mutex
<Box
<dyn ErrorHandler
>>>;
95 pub struct SoundmodemErrorSender
{
97 event_tx
: SyncSender
<SoundmodemEvent
>,
100 impl SoundmodemErrorSender
{
101 pub fn send_error
<E
: Into
<SoundmodemError
>>(&self, err
: E
) {
104 .send(SoundmodemEvent
::RuntimeError(self.source
, err
.into
()));
108 struct PartialKissOut
{
113 impl Read
for Soundmodem
{
114 fn read(&mut self, buf
: &mut [u8]) -> io
::Result
<usize> {
116 let mut partial_kiss_out
= self.partial_kiss_out
.lock().unwrap
();
117 if let Some(partial
) = partial_kiss_out
.as_mut() {
118 let remaining
= partial
.output
.len() - partial
.idx
;
119 let to_write
= remaining
.min(buf
.len());
121 .copy_from_slice(&partial
.output
[partial
.idx
..(partial
.idx
+ to_write
)]);
122 if to_write
== remaining
{
123 *partial_kiss_out
= None
;
125 partial
.idx
+= to_write
;
131 let rx
= self.kiss_out_rx
.lock().unwrap
();
133 .map_err(|s
| io
::Error
::new(ErrorKind
::Other
, format
!("{:?}", s
)))?
135 let to_write
= output
.len().min(buf
.len());
136 buf
[0..to_write
].copy_from_slice(&output
[0..to_write
]);
137 if to_write
!= output
.len() {
138 *self.partial_kiss_out
.lock().unwrap
() = Some(PartialKissOut
{
147 impl Write
for Soundmodem
{
148 fn write(&mut self, buf
: &[u8]) -> std
::io
::Result
<usize> {
149 let _
= self.event_tx
.try_send(SoundmodemEvent
::Kiss(buf
.into
()));
153 fn flush(&mut self) -> std
::io
::Result
<()> {
158 impl Tnc
for Soundmodem
{
159 fn try_clone(&mut self) -> Result
<Self, TncError
> {
161 event_tx
: self.event_tx
.clone(),
162 kiss_out_rx
: self.kiss_out_rx
.clone(),
163 partial_kiss_out
: self.partial_kiss_out
.clone(),
164 error_handler
: self.error_handler
.clone(),
168 fn start(&mut self) {
169 let _
= self.event_tx
.send(SoundmodemEvent
::Start
);
172 fn close(&mut self) {
173 let _
= self.event_tx
.send(SoundmodemEvent
::Close
);
177 pub enum SoundmodemEvent
{
179 BasebandInput(Arc
<[i16]>),
182 DidReadFromOutputBuffer
{ len
: usize, timestamp
: Instant
},
184 RuntimeError(ErrorSource
, SoundmodemError
),
187 fn spawn_soundmodem_worker(
188 event_tx
: SyncSender
<SoundmodemEvent
>,
189 event_rx
: Receiver
<SoundmodemEvent
>,
190 kiss_out_tx
: SyncSender
<Arc
<[u8]>>,
191 input
: Box
<dyn InputSource
>,
192 output
: Box
<dyn OutputSink
>,
193 mut ptt_driver
: Box
<dyn Ptt
>,
194 error_handler
: ErrorHandlerInternal
,
196 std
::thread
::spawn(move || {
197 // TODO: should be able to provide a custom Demodulator for a soundmodem
198 let mut demodulator
= SoftDemodulator
::new();
199 let mut modulator
= SoftModulator
::new();
200 let mut tnc
= SoftTnc
::new();
201 let mut buf
= [0u8; MAX_FRAME_LEN
];
202 let out_buffer
= Arc
::new(RwLock
::new(OutputBuffer
::new()));
203 let mut out_samples
= [0i16; 1024];
204 let start
= Instant
::now();
206 while let Ok(ev
) = event_rx
.recv() {
207 // Update clock on TNC before we do anything
208 let sample_time
= start
.elapsed();
209 let secs
= sample_time
.as_secs();
210 let nanos
= sample_time
.subsec_nanos();
211 // Accurate to within approx 1 sample
212 let now_samples
= 48000 * secs
+ (nanos
as u64 / 20833);
213 tnc
.set_now(now_samples
);
217 SoundmodemEvent
::Kiss(k
) => {
218 let _n
= tnc
.write_kiss(&k
);
219 // TODO: what does it mean if we fail to write it all?
220 // Probably we have to read frames for tx first - revisit this during tx
222 SoundmodemEvent
::BasebandInput(b
) => {
224 if let Some(frame
) = demodulator
.demod(*sample
) {
225 tnc
.handle_frame(frame
);
227 let n
= tnc
.read_kiss(&mut buf
);
229 let _
= kiss_out_tx
.try_send(buf
[0..n
].into
());
236 tnc
.set_data_carrier_detect(demodulator
.data_carrier_detect());
238 SoundmodemEvent
::Start
=> {
239 let input_errors
= SoundmodemErrorSender
{
240 source
: ErrorSource
::Input
,
241 event_tx
: event_tx
.clone(),
243 input
.start(event_tx
.clone(), input_errors
);
244 let output_errors
= SoundmodemErrorSender
{
245 source
: ErrorSource
::Output
,
246 event_tx
: event_tx
.clone(),
248 output
.start(event_tx
.clone(), out_buffer
.clone(), output_errors
);
250 SoundmodemEvent
::Close
=> {
253 if let Err(e
) = ptt_driver
.ptt_off() {
257 .soundmodem_error(ErrorSource
::Ptt
, e
);
261 SoundmodemEvent
::DidReadFromOutputBuffer
{ len
, timestamp
} => {
262 let (occupied
, internal_latency
) = {
263 let out_buffer
= out_buffer
.read().unwrap
();
264 (out_buffer
.samples
.len(), out_buffer
.latency
)
266 let internal_latency
= (internal_latency
.as_secs_f32() * 48000.0) as usize;
267 let dynamic_latency
=
268 len
.saturating_sub((timestamp
.elapsed().as_secs_f32() * 48000.0) as usize);
269 modulator
.update
_o
utp
ut
_b
uffer
(
272 internal_latency
+ dynamic_latency
,
275 SoundmodemEvent
::OutputUnderrun
=> {
276 log
::debug
!("output underrun");
277 // TODO: cancel transmission, send empty data frame to host
279 SoundmodemEvent
::RuntimeError(source
, err
) => {
280 error_handler
.lock().unwrap
().soundmodem_error(source
, err
);
285 let new_ptt
= tnc
.ptt();
288 if let Err(e
) = ptt_driver
.ptt_on() {
292 .soundmodem_error(ErrorSource
::Ptt
, e
);
294 } else if let Err(e
) = ptt_driver
.ptt_off() {
298 .soundmodem_error(ErrorSource
::Ptt
, e
);
303 // Let the modulator do what it wants
304 while let Some(action
) = modulator
.run() {
306 ModulatorAction
::SetIdle(idling
) => {
307 out_buffer
.write().unwrap
().idl
ing
= idling
;
309 ModulatorAction
::GetNextFrame
=> {
310 modulator
.provide_next_frame(tnc
.read_tx_frame());
312 ModulatorAction
::ReadOutput
=> loop {
313 let n
= modulator
.read_output_samples(&mut out_samples
);
317 let mut out_buffer
= out_buffer
.write().unwrap
();
318 for s
in &out_samples
[0..n
] {
319 out_buffer
.samples
.push_back(*s
);
322 ModulatorAction
::TransmissionWillEnd(in_samples
) => {
323 tnc
.set_tx_end_time(in_samples
);
331 pub trait InputSource
: Send
+ Sync
+ '
static {
332 fn start(&self, samples
: SyncSender
<SoundmodemEvent
>, errors
: SoundmodemErrorSender
);
336 pub struct InputRrcFile
{
338 end_tx
: Mutex
<Option
<Sender
<()>>>,
342 pub fn new(path
: PathBuf
) -> Result
<Self, M17Error
> {
343 let mut file
= File
::open(&path
).map_err(|_
| M17Error
::InvalidRrcPath(path
.clone()))?
;
344 let mut baseband
= vec
![];
345 file
.read_to_end(&mut baseband
)
346 .map_err(|_
| M17Error
::RrcReadFailed(path
))?
;
348 baseband
: baseband
.into
(),
349 end_tx
: Mutex
::new(None
),
354 impl InputSource
for InputRrcFile
{
355 fn start(&self, samples
: SyncSender
<SoundmodemEvent
>, errors
: SoundmodemErrorSender
) {
356 let (end_tx
, end_rx
) = channel();
357 let baseband
= self.baseband
.clone();
358 std
::thread
::spawn(move || {
359 // assuming 48 kHz for now
360 const TICK
: Duration
= Duration
::from_millis(25);
361 const SAMPLES_PER_TICK
: usize = 1200;
363 let mut next_tick
= Instant
::now() + TICK
;
364 let mut buf
= [0i16; SAMPLES_PER_TICK
];
367 for sample
in baseband
369 .map(|pair
| i16::from_le_bytes([pair
[0], pair
[1]]))
373 if idx
== SAMPLES_PER_TICK
{
375 .try_send(SoundmodemEvent
::BasebandInput(buf
.into
()))
378 errors
.send_error(InputRrcError
::Overflow
);
382 std
::thread
::sleep(next_tick
.duration_since(Instant
::now()));
384 if end_rx
.try_recv() != Err(TryRecvError
::Empty
) {
389 *self.end_tx
.lock().unwrap
() = Some(end_tx
);
393 let _
= self.end_tx
.lock().unwrap
().take();
397 #[derive(Debug, Error)]
398 pub enum InputRrcError
{
399 #[error("overflow occurred feeding sample to soundmodem")]
403 pub struct NullInputSource
{
404 end_tx
: Mutex
<Option
<Sender
<()>>>,
407 impl NullInputSource
{
408 pub fn new() -> Self {
410 end_tx
: Mutex
::new(None
),
415 impl InputSource
for NullInputSource
{
416 fn start(&self, samples
: SyncSender
<SoundmodemEvent
>, errors
: SoundmodemErrorSender
) {
417 let (end_tx
, end_rx
) = channel();
418 std
::thread
::spawn(move || {
419 // assuming 48 kHz for now
420 const TICK
: Duration
= Duration
::from_millis(25);
421 const SAMPLES_PER_TICK
: usize = 1200;
422 let mut next_tick
= Instant
::now() + TICK
;
425 std
::thread
::sleep(next_tick
.duration_since(Instant
::now()));
427 if end_rx
.try_recv() != Err(TryRecvError
::Empty
) {
431 .try_send(SoundmodemEvent
::BasebandInput(
432 [0i16; SAMPLES_PER_TICK
].into
(),
436 errors
.send_error(NullInputError
::Overflow
);
440 *self.end_tx
.lock().unwrap
() = Some(end_tx
);
444 let _
= self.end_tx
.lock().unwrap
().take();
448 #[derive(Debug, Error)]
449 pub enum NullInputError
{
450 #[error("overflow occurred feeding sample to soundmodem")]
454 impl Default
for NullInputSource
{
455 fn default() -> Self {
460 pub struct OutputBuffer
{
462 // TODO: something more efficient
463 pub samples
: VecDeque
<i16>,
464 pub latency
: Duration
,
468 pub fn new() -> Self {
471 samples
: VecDeque
::new(),
472 latency
: Duration
::ZERO
,
477 impl Default
for OutputBuffer
{
478 fn default() -> Self {
483 pub trait OutputSink
: Send
+ Sync
+ '
static {
486 event_tx
: SyncSender
<SoundmodemEvent
>,
487 buffer
: Arc
<RwLock
<OutputBuffer
>>,
488 errors
: SoundmodemErrorSender
,
493 pub struct OutputRrcFile
{
495 end_tx
: Mutex
<Option
<Sender
<()>>>,
499 pub fn new(path
: PathBuf
) -> Self {
502 end_tx
: Mutex
::new(None
),
507 impl OutputSink
for OutputRrcFile
{
510 event_tx
: SyncSender
<SoundmodemEvent
>,
511 buffer
: Arc
<RwLock
<OutputBuffer
>>,
512 errors
: SoundmodemErrorSender
,
514 let (end_tx
, end_rx
) = channel();
515 let mut file
= match File
::create(self.path
.clone()) {
518 errors
.send_error(OutputRrcError
::Open(e
));
522 std
::thread
::spawn(move || {
523 // assuming 48 kHz for now
524 const TICK
: Duration
= Duration
::from_millis(25);
525 const SAMPLES_PER_TICK
: usize = 1200;
527 // flattened BE i16s for writing
528 let mut buf
= [0u8; SAMPLES_PER_TICK
* 2];
529 let mut next_tick
= Instant
::now() + TICK
;
532 std
::thread
::sleep(next_tick
.duration_since(Instant
::now()));
534 if end_rx
.try_recv() != Err(TryRecvError
::Empty
) {
537 // For now only write deliberately modulated (non-idling) samples
538 // Multiple transmissions will get smooshed together
539 let mut buf_used
= 0;
541 let mut buffer
= buffer
.write().unwrap
();
542 for out
in buf
.chunks_mut(2) {
543 if let Some(s
) = buffer
.samples
.pop_front() {
544 let be
= s
.to_le_bytes();
545 out
.copy_from_slice(&[be
[0], be
[1]]);
547 } else if !buffer
.idl
ing
{
548 let _
= event_tx
.send(SoundmodemEvent
::OutputUnderrun
);
552 if let Err(e
) = file
.write_all(&buf
[0..buf_used
]) {
553 errors
.send_error(OutputRrcError
::WriteError(e
));
556 let _
= event_tx
.send(SoundmodemEvent
::DidReadFromOutputBuffer
{
558 timestamp
: Instant
::now(),
562 *self.end_tx
.lock().unwrap
() = Some(end_tx
);
566 let _
= self.end_tx
.lock().unwrap
().take();
570 #[derive(Debug, Error)]
571 pub enum OutputRrcError
{
572 #[error("unable to open rrc file for writing: {0}")]
573 Open(#[source] std::io::Error),
575 #[error("error writing to output file: {0}")]
576 WriteError(#[source] std::io::Error),
579 pub struct NullOutputSink
{
580 end_tx
: Mutex
<Option
<Sender
<()>>>,
583 impl NullOutputSink
{
584 pub fn new() -> Self {
586 end_tx
: Mutex
::new(None
),
591 impl Default
for NullOutputSink
{
592 fn default() -> Self {
597 impl OutputSink
for NullOutputSink
{
600 event_tx
: SyncSender
<SoundmodemEvent
>,
601 buffer
: Arc
<RwLock
<OutputBuffer
>>,
602 _errors
: SoundmodemErrorSender
,
604 let (end_tx
, end_rx
) = channel();
605 std
::thread
::spawn(move || {
606 // assuming 48 kHz for now
607 const TICK
: Duration
= Duration
::from_millis(25);
608 const SAMPLES_PER_TICK
: usize = 1200;
609 let mut next_tick
= Instant
::now() + TICK
;
612 std
::thread
::sleep(next_tick
.duration_since(Instant
::now()));
614 if end_rx
.try_recv() != Err(TryRecvError
::Empty
) {
618 let mut buffer
= buffer
.write().unwrap
();
620 for _
in 0..SAMPLES_PER_TICK
{
621 if buffer
.samples
.pop_front().is
_none
() {
623 let _
= event_tx
.send(SoundmodemEvent
::OutputUnderrun
);
630 let _
= event_tx
.send(SoundmodemEvent
::DidReadFromOutputBuffer
{
632 timestamp
: Instant
::now(),
636 *self.end_tx
.lock().unwrap
() = Some(end_tx
);
640 let _
= self.end_tx
.lock().unwrap
().take();
644 pub trait Ptt
: Send
+ '
static {
645 fn ptt_on(&mut self) -> Result
<(), SoundmodemError
>;
646 fn ptt_off(&mut self) -> Result
<(), SoundmodemError
>;
649 /// There is no PTT because this TNC will never make transmissions on a real radio.
653 pub fn new() -> Self {
658 impl Default
for NullPtt
{
659 fn default() -> Self {
664 impl Ptt
for NullPtt
{
665 fn ptt_on(&mut self) -> Result
<(), SoundmodemError
> {
669 fn ptt_off(&mut self) -> Result
<(), SoundmodemError
> {