]>
code.octet-stream.net Git - m17rt/blob - m17codec2/src/lib.rs
1 use codec2
::{Codec2
, Codec2Mode
};
3 use m17app
::adapter
::StreamAdapter
;
4 use m17app
::app
::TxHandle
;
5 use m17core
::protocol
::LsfFrame
;
7 use std
::collections
::VecDeque
;
12 mpsc
::{channel
, Receiver
, Sender
},
16 use cpal
::traits
::DeviceTrait
;
17 use cpal
::traits
::HostTrait
;
18 use cpal
::traits
::StreamTrait
;
19 use cpal
::{Sample
, SampleFormat
, SampleRate
};
23 pub fn decode_codec2
<P
: AsRef
<Path
>>(data
: &[u8], out_path
: P
) -> Vec
<i16> {
24 let codec2
= Codec2
::new(Codec2Mode
::MODE_3200
);
25 let var_name
= codec2
;
26 let mut codec
= var_name
;
27 let mut all_samples
: Vec
<i16> = vec
![];
28 for i
in 0..(data
.len() / 8) {
29 let mut samples
= vec
![0; codec
.samples_per_frame()];
30 codec
.decode(&mut samples
, &data
[i
* 8..((i
+ 1) * 8)]);
31 all_samples
.append(&mut samples
);
35 let mut speech_out
= File
::create(out_path
).unwrap
();
36 for b
in &all_samples
{
37 speech_out
.write_all(&b
.to_le_bytes()).unwrap
();
42 pub struct Codec2Adapter
{
43 state
: Arc
<Mutex
<AdapterState
>>,
44 // TODO: make this configurable
49 pub fn new() -> Self {
51 state
: Arc
::new(Mutex
::new(AdapterState
{
53 out_buf
: VecDeque
::new(),
54 codec2
: Codec2
::new(Codec2Mode
::MODE_3200
),
57 output_card
: "default".to_owned(),
64 /// Circular buffer of output samples for playback
65 out_buf
: VecDeque
<i16>,
67 end_tx
: Option
<Sender
<()>>,
70 impl StreamAdapter
for Codec2Adapter
{
71 fn adapter_registered(&self, _id
: usize, handle
: TxHandle
) {
72 self.state
.lock().unwrap
().tx
= Some(handle
);
74 let (end_tx
, end_rx
) = channel();
75 let state
= self.state
.clone();
76 let output_card
= self.output_card
.clone();
77 std
::thread
::spawn(move || stream_thread(end_rx
, state
, output_card
));
78 self.state
.lock().unwrap
().end_tx
= Some(end_tx
);
81 fn adapter_removed(&self) {
82 let mut state
= self.state
.lock().unwrap
();
87 fn tnc_started(&self) {}
89 fn tnc_closed(&self) {}
91 fn stream_began(&self, lsf
: LsfFrame
) {
92 // for now we will assume:
94 // - data type is Voice (Codec2 3200), not Voice+Data
95 // TODO: is encryption handled here or in M17App, such that we get a decrypted stream?
96 // TODO: handle the Voice+Data combination with Codec2 1600
97 self.state
.lock().unwrap
().codec2
= Codec2
::new(Codec2Mode
::MODE_3200
);
100 fn stream_data(&self, frame_number
: u16, is_final
: bool
, data
: Arc
<[u8; 16]>) {
101 let mut state
= self.state
.lock().unwrap
();
102 for encoded
in data
.chunks(8) {
103 if state
.out_buf
.len() < 1024 {
104 let mut samples
= [i16::EQUILIBRIUM
; 160]; // while assuming 3200
105 state
.codec2
.decode(&mut samples
, encoded
);
106 // TODO: maybe get rid of VecDeque so we can decode directly into ring buffer?
108 state
.out_buf
.push_back(s
);
111 debug
!("out_buf overflow");
117 fn output_cb(data
: &mut [i16], state
: &Mutex
<AdapterState
>) {
118 let mut state
= state
.lock().unwrap
();
120 "sound card wants {} samples, we have {} in the buffer",
125 *d
= state
.out_buf
.pop_front().unwrap
_or
(i16::EQUILIBRIUM
);
129 /// Create and manage the stream from a dedicated thread since it's `!Send`
130 fn stream_thread(end
: Receiver
<()>, state
: Arc
<Mutex
<AdapterState
>>, output_card
: String
) {
131 let host
= cpal
::default_host();
135 .find
(|d
| d
.name().unwrap
() == output_card
)
137 let mut configs
= device
.supported_output_configs().unwrap
();
139 .find
(|c
| c
.channels() == 1 && c
.sample_format() == SampleFormat
::I16
)
141 .with_sample_rate(SampleRate(8000));
143 .build_output_stream(
145 move |data
: &mut [i16], _
: &cpal
::OutputCallbackInfo
| {
146 output_cb(data
, &state
);
149 // trigger end_tx here? always more edge cases
150 debug
!("error occurred in codec2 playback: {e:?}");
155 stream
.play().unwrap
();
157 // it seems concrete impls of Stream have a Drop implementation that will handle termination