]>
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();