1 use codec2
::{Codec2
, Codec2Mode
};
2 use cpal
::traits
::DeviceTrait
;
3 use cpal
::traits
::HostTrait
;
4 use cpal
::traits
::StreamTrait
;
5 use cpal
::SampleFormat
;
8 use m17app
::adapter
::StreamAdapter
;
9 use m17app
::app
::TxHandle
;
10 use m17app
::error
::AdapterError
;
11 use m17app
::link_setup
::LinkSetup
;
12 use m17app
::link_setup
::M17Address
;
13 use m17app
::StreamFrame
;
14 use rubato
::Resampler
;
15 use rubato
::SincFixedOut
;
16 use rubato
::SincInterpolationParameters
;
17 use std
::path
::PathBuf
;
19 use std
::sync
::mpsc
::channel
;
22 use std
::time
::Duration
;
23 use std
::time
::Instant
;
25 use crate::M17Codec2Error
;
27 /// Transmits a wave file as an M17 stream
28 pub struct WavePlayer
;
31 /// Plays a wave file (blocking).
33 /// * `path`: wave file to transmit, must be 8 kHz mono and 16-bit LE
34 /// * `tx`: a `TxHandle` obtained from an `M17App`
35 /// * `source`: address of transmission source
36 /// * `destination`: address of transmission destination
37 /// * `channel_access_number`: from 0 to 15, usually 0
42 destination
: &M17Address
,
43 channel_access_number
: u8,
45 let mut reader
= hound
::WavReader
::open(path
).unwrap
();
46 let mut samples
= reader
.samples
::<i16>();
48 let mut codec
= Codec2
::new(Codec2Mode
::MODE_3200
);
49 let mut in_buf
= [0i16; 160];
50 let mut out_buf
= [0u8; 16];
51 let mut lsf_chunk
: usize = 0;
52 const TICK
: Duration
= Duration
::from_millis(40);
53 let mut next_tick
= Instant
::now() + TICK
;
54 let mut frame_number
= 0;
56 let mut setup
= LinkSetup
::new_voice(source
, destination
);
57 setup
.set_channel_access_number(channel_access_number
);
58 tx
.transmit_stream_start(&setup
);
61 let mut last_one
= false;
62 for out
in out_buf
.chunks_mut(8) {
63 for i
in in_buf
.iter
_m
ut
() {
64 let sample
= match samples
.next() {
65 Some(Ok(sample
)) => sample
,
73 codec
.encode(out
, &in_buf
);
75 tx
.transmit_stream_next(&StreamFrame
{
76 lich_idx
: lsf_chunk
as u8,
77 lich_part
: setup
.lich_part(lsf_chunk
as u8),
79 end_of_stream
: last_one
,
83 lsf_chunk
= (lsf_chunk
+ 1) % 6;
89 std
::thread
::sleep(next_tick
.duration_since(Instant
::now()));
95 /// Control transmissions into a Codec2TxAdapter
98 tx
: mpsc
::Sender
<Event
>,
102 pub fn set_ptt(&self, ptt
: bool
) {
103 let _
= self.tx
.send(if ptt
{ Event
::PttOn
} else { Event
::PttOff
});
107 /// Use a microphone and local PTT to transmit Codec2 voice data into an M17 channel.
108 pub struct Codec2TxAdapter
{
109 input_card
: Option
<String
>,
110 event_tx
: mpsc
::Sender
<Event
>,
111 event_rx
: Mutex
<Option
<mpsc
::Receiver
<Event
>>>,
113 destination
: M17Address
,
116 impl Codec2TxAdapter
{
117 pub fn new(source
: M17Address
, destination
: M17Address
) -> Self {
118 let (event_tx
, event_rx
) = mpsc
::channel();
122 event_rx
: Mutex
::new(Some(event_rx
)),
128 pub fn set_input_card
<S
: Into
<String
>>(&mut self, card_name
: S
) {
129 self.inp
ut
_card
= Some(card_name
.into
());
132 pub fn ptt(&self) -> Ptt
{
134 tx
: self.event_tx
.clone(),
142 MicSamples(Arc
<[i16]>),
146 impl StreamAdapter
for Codec2TxAdapter
{
147 fn start(&self, handle
: TxHandle
) -> Result
<(), AdapterError
> {
148 let Some(event_rx
) = self.event_rx
.lock().unwrap
().take() else {
149 return Err(M17Codec2Error
::RepeatStart
.into
());
151 let event_tx
= self.event_tx
.clone();
152 let (setup_tx
, setup_rx
) = channel();
153 let input_card
= self.inp
ut
_card
.clone();
154 let from
= self.source
.clone();
155 let to
= self.destination
.clone();
156 std
::thread
::spawn(move || {
157 stream_thread(event_tx
, event_rx
, setup_tx
, input_card
, handle
, from
, to
)
159 let sample_rate
= setup_rx
.recv()??
;
160 debug
!("selected codec2 microphone sample rate {sample_rate}");
165 fn close(&self) -> Result
<(), AdapterError
> {
166 let _
= self.event_tx
.send(Event
::Close
);
170 fn stream_began(&self, _link_setup
: LinkSetup
) {
171 // not interested in incoming transmissions
174 fn stream_data(&self, _frame_number
: u16, _is_final
: bool
, _data
: Arc
<[u8; 16]>) {
175 // not interested in incoming transmissions
177 // the only reason this is an adapter at all is for future "transmission aborted" feedback
178 // when that's implemented by m17app
183 event_tx
: mpsc
::Sender
<Event
>,
184 event_rx
: mpsc
::Receiver
<Event
>,
185 setup_tx
: mpsc
::Sender
<Result
<u32, AdapterError
>>,
186 input_card
: Option
<String
>,
189 destination
: M17Address
,
191 let host
= cpal
::default_host();
192 let device
= if let Some(input_card
) = input_card
{
193 // TODO: more error handling for unwraps
197 .find
(|d
| d
.name().unwrap
() == input_card
)
201 let _
= setup_tx
.send(Err(M17Codec2Error
::CardUnavailable(input_card
).into
()));
206 match host
.default_input_device() {
209 let _
= setup_tx
.send(Err(M17Codec2Error
::DefaultCardUnavailable
.into
()));
214 let card_name
= device
.name().unwrap
();
215 let mut configs
= match device
.supported_input_configs() {
218 let _
= setup_tx
.send(Err(
219 M17Codec2Error
::InputConfigsUnavailable(card_name
, e
).into
()
224 // TODO: rank these by most favourable, same for rx
225 let config
= match configs
.find
(|c
| {
226 (c
.channels() == 1 || c
.channels() == 2) && c
.sample_format() == SampleFormat
::I16
230 let _
= setup_tx
.send(Err(
231 M17Codec2Error
::SupportedInputUnavailable(card_name
).into
()
237 let target_sample_rate
=
238 if config
.min_sample_rate().0 <= 8000 && config
.max_sample_rate().0 >= 8000 {
241 config
.min_sample_rate().0
243 let channels
= config
.channels();
245 let mut acc
: Box
<dyn Accumulator
> = if target_sample_rate
!= 8000 {
246 Box
::new(ResamplingAccumulator
::new(target_sample_rate
as f64))
248 Box
::new(DirectAccumulator
::new())
251 let config
= config
.with_sample_rate(SampleRate(target_sample_rate
));
252 let stream
= match device
.build_input_stream(
254 move |data
: &[i16], _info
: &cpal
::InputCallbackInfo
| {
255 let mut samples
= vec
![];
256 for d
in data
.chunks(channels
as usize) {
257 // if we were given multi-channel input we'll pick the first channel
258 // TODO: configurable?
261 let _
= event_tx
.send(Event
::MicSamples(samples
.into
()));
265 debug
!("error occurred in codec2 recording: {e:?}");
271 let _
= setup_tx
.send(Err(
272 M17Codec2Error
::InputStreamBuildError(card_name
, e
).into
()
278 let _
= setup_tx
.send(Ok(target_sample_rate
));
279 let mut state
= State
::Idle
;
280 let mut codec2
= Codec2
::new(Codec2Mode
::MODE_3200
);
281 let link_setup
= LinkSetup
::new_voice(&source
, &destination
);
282 let mut lich_idx
= 0;
283 let mut frame_number
= 0;
286 while let Ok(ev
) = event_rx
.recv() {
291 match stream
.play() {
294 // TODO: report M17Codec2Error::InputStreamPlayError(card_name, e).into()
299 codec2
= Codec2
::new(Codec2Mode
::MODE_3200
);
300 state
= State
::StartTransmitting
;
302 State
::StartTransmitting
=> {}
303 State
::Transmitting
=> {}
304 State
::Ending
=> state
= State
::EndingWithPttRestart
,
305 State
::EndingWithPttRestart
=> {}
308 Event
::PttOff
=> match state
{
310 State
::StartTransmitting
=> state
= State
::Idle
,
311 State
::Transmitting
=> state
= State
::Ending
,
313 State
::EndingWithPttRestart
=> state
= State
::Ending
,
315 Event
::MicSamples(samples
) => {
318 State
::StartTransmitting
319 | State
::Transmitting
321 | State
::EndingWithPttRestart
=> {
322 acc
.handle_samples(&samples
);
323 while let Some(frame
) = acc
.try_next_frame() {
324 let mut stream_data
= [0u8; 16];
325 codec2
.encode(&mut stream_data
[0..8], &frame
[0..160]);
326 codec2
.encode(&mut stream_data
[8..16], &frame
[160..320]);
328 if state
== State
::StartTransmitting
{
329 handle
.transmit_stream_start(&link_setup
);
332 state
= State
::Transmitting
;
335 let end_of_stream
= state
!= State
::Transmitting
;
336 handle
.transmit_stream_next(&StreamFrame
{
338 lich_part
: link_setup
.lich_part(lich_idx
),
344 lich_idx
= (lich_idx
+ 1) % 6;
351 if state
== State
::Ending
{
352 // when finished sending final stream frame
353 let _
= stream
.pause();
357 if state
== State
::EndingWithPttRestart
{
359 codec2
= Codec2
::new(Codec2Mode
::MODE_3200
);
360 state
= State
::StartTransmitting
;
366 // assume PTT etc. will clean up itself responsibly on close
373 #[derive(Debug, PartialEq, Eq)]
377 /// PTT engaged but we are collecting the first full frame of audio data before starting the stream
379 /// Streaming voice frames
381 /// PTT disengaged; we are collecting the next frame of audio to use as a final frame
383 /// PTT was re-enaged while ending; we will send final frame then go back to StartTransmitting
384 EndingWithPttRestart
,
387 fn resampler_params() -> SincInterpolationParameters
{
388 SincInterpolationParameters
{
391 oversampling_factor
: 128,
392 interpolation
: rubato
::SincInterpolationType
::Cubic
,
393 window
: rubato
::WindowFunction
::BlackmanHarris2
,
398 fn handle_samples(&mut self, samples
: &[i16]);
399 /// Return 320 samples, enough for two Codec2 frames
400 fn try_next_frame(&mut self) -> Option
<Vec
<i16>>;
404 struct DirectAccumulator
{
408 impl DirectAccumulator
{
410 Self { buffer
: Vec
::new() }
414 impl Accumulator
for DirectAccumulator
{
415 fn handle_samples(&mut self, samples
: &[i16]) {
416 self.buffer
.extend_from_slice(samples
);
419 fn try_next_frame(&mut self) -> Option
<Vec
<i16>> {
420 if self.buffer
.len() >= 320 {
421 let part
= self.buffer
.split_off(320);
422 Some(std
::mem
::replace(&mut self.buffer
, part
))
428 fn reset(&mut self) {
433 struct ResamplingAccumulator
{
436 resampler
: SincFixedOut
<f32>,
439 impl ResamplingAccumulator
{
440 fn new(input_rate
: f64) -> Self {
444 resampler
: make_resampler(input_rate
),
449 impl Accumulator
for ResamplingAccumulator
{
450 fn handle_samples(&mut self, samples
: &[i16]) {
451 self.buffer
.extend_from_slice(samples
);
454 fn try_next_frame(&mut self) -> Option
<Vec
<i16>> {
455 let required
= self.resampler
.inp
ut
_frames
_next
();
456 if self.buffer
.len() >= required
{
457 let mut part
= self.buffer
.split_off(required
);
458 std
::mem
::swap(&mut self.buffer
, &mut part
);
459 let samples_f
: Vec
<f32> = part
.iter
().map(|s
| *s
as f32 / 16384.0f32).collect();
460 let out
= self.resampler
.process(&[samples_f
], None
).unwrap
();
461 Some(out
[0].iter
().map(|s
| (*s
* 16383.0f32) as i16).collect())
467 fn reset(&mut self) {
469 self.resampler
= make_resampler(self.inp
ut
_rate
);
473 fn make_resampler(input_rate
: f64) -> SincFixedOut
<f32> {
474 // want 320 samples at a time to create 2x Codec2 frames per M17 Voice frame
475 SincFixedOut
::new(8000f64 / input_rate
, 1.0, resampler_params(), 320, 1).unwrap
()