X-Git-Url: https://code.octet-stream.net/m17rt/blobdiff_plain/bc4b6ea4290384cd8c380af2a9014f162fd43683..3903e719137aba15d30dd58b8d917965ec602400:/m17codec2/src/lib.rs diff --git a/m17codec2/src/lib.rs b/m17codec2/src/lib.rs index 5146bca..e4bfbac 100755 --- a/m17codec2/src/lib.rs +++ b/m17codec2/src/lib.rs @@ -1,3 +1,5 @@ +#![doc = include_str!("../README.md")] + use codec2::{Codec2, Codec2Mode}; use cpal::traits::DeviceTrait; use cpal::traits::HostTrait; @@ -6,10 +8,9 @@ use cpal::{Sample, SampleFormat, SampleRate}; use log::debug; use m17app::adapter::StreamAdapter; use m17app::app::TxHandle; -use m17core::address::Address; -use m17core::address::Callsign; -use m17core::protocol::LsfFrame; -use m17core::protocol::StreamFrame; +use m17app::link_setup::LinkSetup; +use m17app::link_setup::M17Address; +use m17app::StreamFrame; use std::collections::VecDeque; use std::fs::File; use std::io::Write; @@ -41,6 +42,7 @@ pub fn decode_codec2>(data: &[u8], out_path: P) -> Vec { all_samples } +/// Subscribes to M17 streams and attempts to play the decoded Codec2 pub struct Codec2Adapter { state: Arc>, // TODO: make this configurable @@ -62,6 +64,12 @@ impl Codec2Adapter { } } +impl Default for Codec2Adapter { + fn default() -> Self { + Self::new() + } +} + struct AdapterState { tx: Option, /// Circular buffer of output samples for playback @@ -91,7 +99,7 @@ impl StreamAdapter for Codec2Adapter { fn tnc_closed(&self) {} - fn stream_began(&self, _lsf: LsfFrame) { + fn stream_began(&self, _link_setup: LinkSetup) { // for now we will assume: // - unencrypted // - data type is Voice (Codec2 3200), not Voice+Data @@ -157,10 +165,24 @@ fn stream_thread(end: Receiver<()>, state: Arc>, output_card // it seems concrete impls of Stream have a Drop implementation that will handle termination } +/// Transmits a wave file as an M17 stream pub struct WavePlayer; impl WavePlayer { - pub fn play(path: PathBuf, tx: TxHandle) { + /// Plays a wave file (blocking). + /// + /// * `path`: wave file to transmit, must be 8 kHz mono and 16-bit LE + /// * `tx`: a `TxHandle` obtained from an `M17App` + /// * `source`: address of transmission source + /// * `destination`: address of transmission destination + /// * `channel_access_number`: from 0 to 15, usually 0 + pub fn play( + path: PathBuf, + tx: TxHandle, + source: &M17Address, + destination: &M17Address, + channel_access_number: u8, + ) { let mut reader = hound::WavReader::open(path).unwrap(); let mut samples = reader.samples::(); @@ -172,19 +194,14 @@ impl WavePlayer { let mut next_tick = Instant::now() + TICK; let mut frame_number = 0; - // TODO: need a better way to create addresses from std strings - - let lsf = LsfFrame::new_voice( - &Address::Callsign(Callsign(b"VK7XT ".clone())), - &Address::Broadcast, - ); - - tx.transmit_stream_start(lsf.clone()); + let mut setup = LinkSetup::new_voice(source, destination); + setup.set_channel_access_number(channel_access_number); + tx.transmit_stream_start(&setup); loop { let mut last_one = false; - for mut out in out_buf.chunks_mut(8) { - for i in 0..160 { + for out in out_buf.chunks_mut(8) { + for i in in_buf.iter_mut() { let sample = match samples.next() { Some(Ok(sample)) => sample, _ => { @@ -192,18 +209,16 @@ impl WavePlayer { 0 } }; - in_buf[i] = sample; + *i = sample; } - codec.encode(&mut out, &in_buf); + codec.encode(out, &in_buf); } - tx.transmit_stream_next(StreamFrame { + tx.transmit_stream_next(&StreamFrame { lich_idx: lsf_chunk as u8, - lich_part: lsf.0[lsf_chunk * 5..(lsf_chunk + 1) * 5] - .try_into() - .unwrap(), + lich_part: setup.lich_part(lsf_chunk as u8), frame_number, end_of_stream: last_one, - stream_data: out_buf.clone(), + stream_data: out_buf, }); frame_number += 1; lsf_chunk = (lsf_chunk + 1) % 6;