]> code.octet-stream.net Git - m17rt/blobdiff - m17codec2/src/lib.rs
Fix timing bugs and add documentation
[m17rt] / m17codec2 / src / lib.rs
index 5146bcaac5678f7cbfc70b4e5eec5b2ae9e74c49..e4bfbacc76f1a0cf2f28f7a7915672b4d41eb82b 100755 (executable)
@@ -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<P: AsRef<Path>>(data: &[u8], out_path: P) -> Vec<i16> {
     all_samples
 }
 
+/// Subscribes to M17 streams and attempts to play the decoded Codec2
 pub struct Codec2Adapter {
     state: Arc<Mutex<AdapterState>>,
     // TODO: make this configurable
@@ -62,6 +64,12 @@ impl Codec2Adapter {
     }
 }
 
+impl Default for Codec2Adapter {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
 struct AdapterState {
     tx: Option<TxHandle>,
     /// 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<Mutex<AdapterState>>, 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::<i16>();
 
@@ -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;