]> code.octet-stream.net Git - m17rt/commitdiff
New Soundcard struct, use it in test programs
authorThomas Karpiniec <tom.karpiniec@outlook.com>
Thu, 23 Jan 2025 22:37:24 +0000 (09:37 +1100)
committerThomas Karpiniec <tom.karpiniec@outlook.com>
Thu, 23 Jan 2025 22:37:24 +0000 (09:37 +1100)
15 files changed:
Cargo.lock
Cargo.toml
m17app/src/adapter.rs
m17app/src/error.rs
m17app/src/lib.rs
m17app/src/soundcard.rs [new file with mode: 0644]
m17app/src/soundmodem.rs
m17codec2/src/lib.rs
m17core/src/tnc.rs
tools/m17rt-demod/src/main.rs
tools/m17rt-mod/src/main.rs
tools/m17rt-rxpacket/src/main.rs
tools/m17rt-soundcards/Cargo.toml [new file with mode: 0644]
tools/m17rt-soundcards/src/main.rs [new file with mode: 0644]
tools/m17rt-txpacket/src/main.rs

index a1a9c8b72c3adcb7e94ebaa608670ef29dea7630..6a2bcd0268be20a801b7c56ced75c0ed3372717a 100755 (executable)
@@ -516,6 +516,13 @@ dependencies = [
  "m17core",
 ]
 
+[[package]]
+name = "m17rt-soundcards"
+version = "0.1.0"
+dependencies = [
+ "m17app",
+]
+
 [[package]]
 name = "m17rt-txpacket"
 version = "0.1.0"
index 8d1b91d605c7fd98ee081979b474d629b45bf4d6..fab714681ec9003c2f2b5981eb14f494e195bf98 100755 (executable)
@@ -1,5 +1,5 @@
 [workspace]
 resolver = "2"
 members = [
-    "m17app", "m17codec2", "m17core", "tools/m17rt-demod", "tools/m17rt-mod", "tools/m17rt-txpacket", "tools/m17rt-rxpacket"
+    "m17app", "m17codec2", "m17core", "tools/m17rt-demod", "tools/m17rt-mod", "tools/m17rt-txpacket", "tools/m17rt-rxpacket", "tools/m17rt-soundcards"
 ]
index 57e01bbbabae6e35947a07ceacdb50219446e67b..8fdf9d9a3c7064a92fba563cd0b00044cec008c5 100644 (file)
@@ -3,26 +3,36 @@ use m17core::protocol::PacketType;
 use std::sync::Arc;
 
 pub trait PacketAdapter: Send + Sync + 'static {
-    fn adapter_registered(&self, _id: usize, _handle: TxHandle) {}
+    fn adapter_registered(&self, id: usize, handle: TxHandle) {
+        let _ = id;
+        let _ = handle;
+    }
     fn adapter_removed(&self) {}
     fn tnc_started(&self) {}
     fn tnc_closed(&self) {}
-    fn packet_received(
-        &self,
-        _link_setup: LinkSetup,
-        _packet_type: PacketType,
-        _content: Arc<[u8]>,
-    ) {
+    fn packet_received(&self, link_setup: LinkSetup, packet_type: PacketType, content: Arc<[u8]>) {
+        let _ = link_setup;
+        let _ = packet_type;
+        let _ = content;
     }
 }
 
 pub trait StreamAdapter: Send + Sync + 'static {
-    fn adapter_registered(&self, _id: usize, _handle: TxHandle) {}
+    fn adapter_registered(&self, id: usize, handle: TxHandle) {
+        let _ = id;
+        let _ = handle;
+    }
     fn adapter_removed(&self) {}
     fn tnc_started(&self) {}
     fn tnc_closed(&self) {}
-    fn stream_began(&self, _link_setup: LinkSetup) {}
-    fn stream_data(&self, _frame_number: u16, _is_final: bool, _data: Arc<[u8; 16]>) {}
+    fn stream_began(&self, link_setup: LinkSetup) {
+        let _ = link_setup;
+    }
+    fn stream_data(&self, frame_number: u16, is_final: bool, data: Arc<[u8; 16]>) {
+        let _ = frame_number;
+        let _ = is_final;
+        let _ = data;
+    }
 
     // TODO
     // fn stream_lost(&self);
index f7079ca3ca8afe6cbe6a7e92c13285a91602760e..ee624f9257c9a873824cb475fd2f471268a629d0 100644 (file)
@@ -7,4 +7,10 @@ pub enum M17Error {
 
     #[error("given callsign is {0} characters long; maximum is 9")]
     CallsignTooLong(usize),
+
+    #[error("error during soundcard initialisation")]
+    SoundcardInit,
+
+    #[error("unable to locate sound card '{0}' - is it in use?")]
+    SoundcardNotFound(String),
 }
index ce67840994f63ec28e5f62bd8c6c8a86470e5f6e..543bdc5f1383f2036675fcabf63ee8259d9795d8 100755 (executable)
@@ -3,5 +3,9 @@ pub mod app;
 pub mod error;
 pub mod link_setup;
 pub mod serial;
+pub mod soundcard;
 pub mod soundmodem;
 pub mod tnc;
+
+// Protocol definitions needed to implement stream and packet adapters or create fully custom LSFs
+pub use m17core::protocol::{LsfFrame, PacketType, StreamFrame};
diff --git a/m17app/src/soundcard.rs b/m17app/src/soundcard.rs
new file mode 100644 (file)
index 0000000..b12ef88
--- /dev/null
@@ -0,0 +1,261 @@
+use std::{
+    sync::{
+        mpsc::{sync_channel, Receiver, SyncSender},
+        Arc, RwLock,
+    },
+    time::{Duration, Instant},
+};
+
+use cpal::{
+    traits::{DeviceTrait, HostTrait, StreamTrait},
+    SampleFormat, SampleRate, Stream,
+};
+
+use crate::{
+    error::M17Error,
+    soundmodem::{InputSource, OutputBuffer, OutputSink, SoundmodemEvent},
+};
+
+pub struct Soundcard {
+    event_tx: SyncSender<SoundcardEvent>,
+}
+
+impl Soundcard {
+    pub fn new<S: Into<String>>(card_name: S) -> Result<Self, M17Error> {
+        let (card_tx, card_rx) = sync_channel(128);
+        let (setup_tx, setup_rx) = sync_channel(1);
+        spawn_soundcard_worker(card_rx, setup_tx, card_name.into());
+        match setup_rx.recv() {
+            Ok(Ok(())) => Ok(Self { event_tx: card_tx }),
+            Ok(Err(e)) => Err(e),
+            Err(_) => Err(M17Error::SoundcardInit),
+        }
+    }
+
+    pub fn input(&self) -> SoundcardInputSource {
+        SoundcardInputSource {
+            event_tx: self.event_tx.clone(),
+        }
+    }
+
+    pub fn output(&self) -> SoundcardOutputSink {
+        SoundcardOutputSink {
+            event_tx: self.event_tx.clone(),
+        }
+    }
+
+    pub fn set_rx_inverted(&self, inverted: bool) {
+        let _ = self.event_tx.send(SoundcardEvent::SetRxInverted(inverted));
+    }
+
+    pub fn set_tx_inverted(&self, inverted: bool) {
+        let _ = self.event_tx.send(SoundcardEvent::SetTxInverted(inverted));
+    }
+
+    pub fn supported_output_cards() -> Vec<String> {
+        let mut out = vec![];
+        let host = cpal::default_host();
+        let Ok(output_devices) = host.output_devices() else {
+            return out;
+        };
+        for d in output_devices {
+            let Ok(mut configs) = d.supported_output_configs() else {
+                continue;
+            };
+            if configs
+                .find(|config| {
+                    config.channels() == 1 && config.sample_format() == SampleFormat::I16
+                })
+                .is_some()
+            {
+                let Ok(name) = d.name() else {
+                    continue;
+                };
+                out.push(name);
+            }
+        }
+        out.sort();
+        out
+    }
+
+    pub fn supported_input_cards() -> Vec<String> {
+        let mut out = vec![];
+        let host = cpal::default_host();
+        let Ok(input_devices) = host.input_devices() else {
+            return out;
+        };
+        for d in input_devices {
+            let Ok(mut configs) = d.supported_input_configs() else {
+                continue;
+            };
+            if configs
+                .find(|config| {
+                    config.channels() == 1 && config.sample_format() == SampleFormat::I16
+                })
+                .is_some()
+            {
+                let Ok(name) = d.name() else {
+                    continue;
+                };
+                out.push(name);
+            }
+        }
+        out.sort();
+        out
+    }
+}
+
+enum SoundcardEvent {
+    SetRxInverted(bool),
+    SetTxInverted(bool),
+    StartInput {
+        samples: SyncSender<SoundmodemEvent>,
+    },
+    CloseInput,
+    StartOutput {
+        event_tx: SyncSender<SoundmodemEvent>,
+        buffer: Arc<RwLock<OutputBuffer>>,
+    },
+    CloseOutput,
+}
+
+pub struct SoundcardInputSource {
+    event_tx: SyncSender<SoundcardEvent>,
+}
+
+impl InputSource for SoundcardInputSource {
+    fn start(&self, samples: SyncSender<SoundmodemEvent>) {
+        let _ = self.event_tx.send(SoundcardEvent::StartInput { samples });
+    }
+
+    fn close(&self) {
+        let _ = self.event_tx.send(SoundcardEvent::CloseInput);
+    }
+}
+
+pub struct SoundcardOutputSink {
+    event_tx: SyncSender<SoundcardEvent>,
+}
+
+impl OutputSink for SoundcardOutputSink {
+    fn start(&self, event_tx: SyncSender<SoundmodemEvent>, buffer: Arc<RwLock<OutputBuffer>>) {
+        let _ = self
+            .event_tx
+            .send(SoundcardEvent::StartOutput { event_tx, buffer });
+    }
+
+    fn close(&self) {
+        let _ = self.event_tx.send(SoundcardEvent::CloseOutput);
+    }
+}
+
+fn spawn_soundcard_worker(
+    event_rx: Receiver<SoundcardEvent>,
+    setup_tx: SyncSender<Result<(), M17Error>>,
+    card_name: String,
+) {
+    std::thread::spawn(move || {
+        let host = cpal::default_host();
+        let Some(device) = host
+            .devices()
+            .unwrap()
+            .find(|d| d.name().unwrap() == card_name)
+        else {
+            let _ = setup_tx.send(Err(M17Error::SoundcardNotFound(card_name)));
+            return;
+        };
+
+        let _ = setup_tx.send(Ok(()));
+        let mut rx_inverted = false;
+        let mut tx_inverted = false;
+        let mut input_stream: Option<Stream> = None;
+        let mut output_stream: Option<Stream> = None;
+
+        while let Ok(ev) = event_rx.recv() {
+            match ev {
+                SoundcardEvent::SetRxInverted(inv) => rx_inverted = inv,
+                SoundcardEvent::SetTxInverted(inv) => tx_inverted = inv,
+                SoundcardEvent::StartInput { samples } => {
+                    let mut input_configs = device.supported_input_configs().unwrap();
+                    let input_config = input_configs
+                        .find(|c| c.channels() == 1 && c.sample_format() == SampleFormat::I16)
+                        .unwrap()
+                        .with_sample_rate(SampleRate(48000));
+                    let stream = device
+                        .build_input_stream(
+                            &input_config.into(),
+                            move |data: &[i16], _info: &cpal::InputCallbackInfo| {
+                                let out: Vec<i16> = data
+                                    .iter()
+                                    .map(|s| if rx_inverted { s.saturating_neg() } else { *s })
+                                    .collect();
+                                let _ =
+                                    samples.try_send(SoundmodemEvent::BasebandInput(out.into()));
+                            },
+                            |e| {
+                                // TODO: abort?
+                                log::debug!("error occurred in soundcard input: {e:?}");
+                            },
+                            None,
+                        )
+                        .unwrap();
+                    stream.play().unwrap();
+                    input_stream = Some(stream);
+                }
+                SoundcardEvent::CloseInput => {
+                    let _ = input_stream.take();
+                }
+                SoundcardEvent::StartOutput { event_tx, buffer } => {
+                    let mut output_configs = device.supported_output_configs().unwrap();
+                    // TODO: more error handling
+                    let output_config = output_configs
+                        .find(|c| c.channels() == 1 && c.sample_format() == SampleFormat::I16)
+                        .unwrap()
+                        .with_sample_rate(SampleRate(48000));
+                    let stream = device
+                        .build_output_stream(
+                            &output_config.into(),
+                            move |data: &mut [i16], info: &cpal::OutputCallbackInfo| {
+                                let mut taken = 0;
+                                let ts = info.timestamp();
+                                let latency = ts
+                                    .playback
+                                    .duration_since(&ts.callback)
+                                    .unwrap_or(Duration::ZERO);
+                                let mut buffer = buffer.write().unwrap();
+                                buffer.latency = latency;
+                                for out in data.iter_mut() {
+                                    if let Some(s) = buffer.samples.pop_front() {
+                                        *out = if tx_inverted { s.saturating_neg() } else { s };
+                                        taken += 1;
+                                    } else if buffer.idling {
+                                        *out = 0;
+                                    } else {
+                                        log::debug!("output soundcard had underrun");
+                                        let _ = event_tx.send(SoundmodemEvent::OutputUnderrun);
+                                        break;
+                                    }
+                                }
+                                //debug!("latency is {} ms, taken {taken}", latency.as_millis());
+                                let _ = event_tx.send(SoundmodemEvent::DidReadFromOutputBuffer {
+                                    len: taken,
+                                    timestamp: Instant::now(),
+                                });
+                            },
+                            |e| {
+                                // TODO: abort?
+                                log::debug!("error occurred in soundcard output: {e:?}");
+                            },
+                            None,
+                        )
+                        .unwrap();
+                    stream.play().unwrap();
+                    output_stream = Some(stream);
+                }
+                SoundcardEvent::CloseOutput => {
+                    let _ = output_stream.take();
+                }
+            }
+        }
+    });
+}
index dced157eeab6cee20fa3607a3c6acfcebbcf15cd..2d005b1b31b408f2134bd49ff5096659a4ffe876 100644 (file)
@@ -1,8 +1,4 @@
 use crate::tnc::{Tnc, TncError};
-use cpal::traits::DeviceTrait;
-use cpal::traits::HostTrait;
-use cpal::traits::StreamTrait;
-use cpal::{SampleFormat, SampleRate};
 use log::debug;
 use m17core::kiss::MAX_FRAME_LEN;
 use m17core::modem::{Demodulator, Modulator, ModulatorAction, SoftDemodulator, SoftModulator};
@@ -239,72 +235,6 @@ pub trait InputSource: Send + Sync + 'static {
     fn close(&self);
 }
 
-pub struct InputSoundcard {
-    // TODO: allow for inversion both here and in output
-    cpal_name: Option<String>,
-    end_tx: Mutex<Option<Sender<()>>>,
-}
-
-impl InputSoundcard {
-    pub fn new() -> Self {
-        Self {
-            cpal_name: None,
-            end_tx: Mutex::new(None),
-        }
-    }
-
-    pub fn new_with_card(card_name: String) -> Self {
-        Self {
-            cpal_name: Some(card_name),
-            end_tx: Mutex::new(None),
-        }
-    }
-}
-
-impl InputSource for InputSoundcard {
-    fn start(&self, samples: SyncSender<SoundmodemEvent>) {
-        let (end_tx, end_rx) = channel();
-        let cpal_name = self.cpal_name.clone();
-        std::thread::spawn(move || {
-            let host = cpal::default_host();
-            let device = if let Some(name) = cpal_name.as_deref() {
-                host.input_devices()
-                    .unwrap()
-                    .find(|d| d.name().unwrap() == name)
-                    .unwrap()
-            } else {
-                host.default_input_device().unwrap()
-            };
-            let mut configs = device.supported_input_configs().unwrap();
-            let config = configs
-                .find(|c| c.channels() == 1 && c.sample_format() == SampleFormat::I16)
-                .unwrap()
-                .with_sample_rate(SampleRate(48000));
-            let stream = device
-                .build_input_stream(
-                    &config.into(),
-                    move |data: &[i16], _info: &cpal::InputCallbackInfo| {
-                        let out: Vec<i16> = data.iter().map(|s| *s).collect();
-                        let _ = samples.try_send(SoundmodemEvent::BasebandInput(out.into()));
-                    },
-                    |e| {
-                        // TODO: abort?
-                        debug!("error occurred in soundcard input: {e:?}");
-                    },
-                    None,
-                )
-                .unwrap();
-            stream.play().unwrap();
-            let _ = end_rx.recv();
-        });
-        *self.end_tx.lock().unwrap() = Some(end_tx);
-    }
-
-    fn close(&self) {
-        let _ = self.end_tx.lock().unwrap().take();
-    }
-}
-
 pub struct InputRrcFile {
     path: PathBuf,
     end_tx: Mutex<Option<Sender<()>>>,
@@ -407,10 +337,10 @@ impl InputSource for NullInputSource {
 }
 
 pub struct OutputBuffer {
-    idling: bool,
+    pub idling: bool,
     // TODO: something more efficient
-    samples: VecDeque<i16>,
-    latency: Duration,
+    pub samples: VecDeque<i16>,
+    pub latency: Duration,
 }
 
 impl OutputBuffer {
@@ -553,96 +483,6 @@ impl OutputSink for NullOutputSink {
     }
 }
 
-pub struct OutputSoundcard {
-    // TODO: allow for inversion both here and in output
-    cpal_name: Option<String>,
-    end_tx: Mutex<Option<Sender<()>>>,
-}
-
-impl OutputSoundcard {
-    pub fn new() -> Self {
-        Self {
-            cpal_name: None,
-            end_tx: Mutex::new(None),
-        }
-    }
-
-    pub fn new_with_card(card_name: String) -> Self {
-        Self {
-            cpal_name: Some(card_name),
-            end_tx: Mutex::new(None),
-        }
-    }
-}
-
-impl OutputSink for OutputSoundcard {
-    fn start(&self, event_tx: SyncSender<SoundmodemEvent>, buffer: Arc<RwLock<OutputBuffer>>) {
-        let (end_tx, end_rx) = channel();
-        let cpal_name = self.cpal_name.clone();
-        std::thread::spawn(move || {
-            let host = cpal::default_host();
-            let device = if let Some(name) = cpal_name.as_deref() {
-                host.output_devices()
-                    .unwrap()
-                    .find(|d| d.name().unwrap() == name)
-                    .unwrap()
-            } else {
-                host.default_output_device().unwrap()
-            };
-            let mut configs = device.supported_output_configs().unwrap();
-            // TODO: more error handling
-            let config = configs
-                .find(|c| c.channels() == 1 && c.sample_format() == SampleFormat::I16)
-                .unwrap()
-                .with_sample_rate(SampleRate(48000));
-            let stream = device
-                .build_output_stream(
-                    &config.into(),
-                    move |data: &mut [i16], info: &cpal::OutputCallbackInfo| {
-                        let mut taken = 0;
-                        let ts = info.timestamp();
-                        let latency = ts
-                            .playback
-                            .duration_since(&ts.callback)
-                            .unwrap_or(Duration::ZERO);
-                        let mut buffer = buffer.write().unwrap();
-                        buffer.latency = latency;
-                        for out in data.iter_mut() {
-                            if let Some(s) = buffer.samples.pop_front() {
-                                *out = s;
-                                taken += 1;
-                            } else if buffer.idling {
-                                *out = 0;
-                            } else {
-                                debug!("output soundcard had underrun");
-                                let _ = event_tx.send(SoundmodemEvent::OutputUnderrun);
-                                break;
-                            }
-                        }
-                        //debug!("latency is {} ms, taken {taken}", latency.as_millis());
-                        let _ = event_tx.send(SoundmodemEvent::DidReadFromOutputBuffer {
-                            len: taken,
-                            timestamp: Instant::now(),
-                        });
-                    },
-                    |e| {
-                        // TODO: abort?
-                        debug!("error occurred in soundcard output: {e:?}");
-                    },
-                    None,
-                )
-                .unwrap();
-            stream.play().unwrap();
-            let _ = end_rx.recv();
-        });
-        *self.end_tx.lock().unwrap() = Some(end_tx);
-    }
-
-    fn close(&self) {
-        let _ = self.end_tx.lock().unwrap().take();
-    }
-}
-
 pub trait Ptt: Send + 'static {
     fn ptt_on(&mut self);
     fn ptt_off(&mut self);
index 0f3c8a5e2cb0199e7754fa3289d80818f8123d00..016acc78c49c2869ed0d58c78b2ac2705f5d7d62 100755 (executable)
@@ -8,10 +8,7 @@ use m17app::adapter::StreamAdapter;
 use m17app::app::TxHandle;
 use m17app::link_setup::LinkSetup;
 use m17app::link_setup::M17Address;
-use m17core::address::Address;
-use m17core::address::Callsign;
-use m17core::protocol::LsfFrame;
-use m17core::protocol::StreamFrame;
+use m17app::StreamFrame;
 use std::collections::VecDeque;
 use std::fs::File;
 use std::io::Write;
index 93a4363ae62f0939100ee51a04166fc548595edd..8965499f2d7045b5d86fb5c4fad2a1ec93dfe3bb 100644 (file)
@@ -102,6 +102,10 @@ impl SoftTnc {
 
     /// Process an individual `Frame` that has been decoded by the modem.
     pub fn handle_frame(&mut self, frame: Frame) {
+        if self.ptt {
+            // Ignore self-decodes
+            return;
+        }
         match frame {
             Frame::Lsf(lsf) => {
                 // A new LSF implies a clean slate.
index 9c229af22dd1d990610d7684e05b11ee334cb5d3..af5fafd485a0f05c52b620cb33e42f45754336e0 100755 (executable)
@@ -1,22 +1,21 @@
 use m17app::app::M17App;
-use m17app::soundmodem::{InputRrcFile, InputSoundcard, NullOutputSink, NullPtt, Soundmodem};
+use m17app::soundcard::Soundcard;
+use m17app::soundmodem::{NullOutputSink, NullPtt, Soundmodem};
 use m17codec2::Codec2Adapter;
-use std::path::PathBuf;
 
-pub fn m17app_test() {
-    //let path = PathBuf::from("../../../Data/test_vk7xt.rrc");
-    let path = PathBuf::from("../../../Data/mymod.rrc");
-    //let path = PathBuf::from("../../../Data/mymod-noisy.raw");
-    let source = InputRrcFile::new(path);
-    //let source = InputSoundcard::new();
-    let soundmodem = Soundmodem::new(source, NullOutputSink::new(), NullPtt::new());
+pub fn demod_test() {
+    let soundcard = Soundcard::new("plughw:CARD=Device,DEV=0").unwrap();
+    let soundmodem = Soundmodem::new(soundcard.input(), NullOutputSink::new(), NullPtt::new());
     let app = M17App::new(soundmodem);
     app.add_stream_adapter(Codec2Adapter::new());
     app.start();
-    std::thread::sleep(std::time::Duration::from_secs(15));
+
+    loop {
+        std::thread::park();
+    }
 }
 
 fn main() {
     env_logger::init();
-    m17app_test();
+    demod_test();
 }
index c616b2e8b0110247a8b40f67cf8c7170dd0aeae7..50358bd7a34634c916c75b9367b2f7209b86df79 100644 (file)
@@ -1,31 +1,29 @@
 use m17app::app::M17App;
 use m17app::link_setup::M17Address;
-use m17app::soundmodem::{
-    InputRrcFile, InputSoundcard, NullInputSource, NullOutputSink, NullPtt, OutputRrcFile,
-    OutputSoundcard, Soundmodem,
-};
-use m17codec2::{Codec2Adapter, WavePlayer};
+use m17app::serial::{PttPin, SerialPtt};
+use m17app::soundcard::Soundcard;
+use m17app::soundmodem::Soundmodem;
+use m17codec2::WavePlayer;
 use std::path::PathBuf;
 
 pub fn mod_test() {
-    let in_path = PathBuf::from("../../../Data/test_vk7xt_8k.wav");
-    let out_path = PathBuf::from("../../../Data/mymod.rrc");
-    let output = OutputRrcFile::new(out_path);
-    //let output = OutputSoundcard::new();
-    let soundmodem = Soundmodem::new(NullInputSource::new(), output, NullPtt::new());
+    let soundcard = Soundcard::new("plughw:CARD=Device,DEV=0").unwrap();
+    let ptt = SerialPtt::new("/dev/ttyUSB0", PttPin::Rts);
+    let soundmodem = Soundmodem::new(soundcard.input(), soundcard.output(), ptt);
     let app = M17App::new(soundmodem);
     app.start();
     std::thread::sleep(std::time::Duration::from_secs(1));
     println!("Beginning playback...");
     WavePlayer::play(
-        in_path,
+        PathBuf::from("../../../Data/test_vk7xt_8k.wav"),
         app.tx(),
-        &M17Address::from_callsign("VK7XT").unwrap(),
+        &M17Address::from_callsign("VK7XT-1").unwrap(),
         &M17Address::new_broadcast(),
         0,
     );
-    println!("Playback complete, terminating in 5 secs");
-    std::thread::sleep(std::time::Duration::from_secs(5));
+    println!("Playback complete.");
+    std::thread::sleep(std::time::Duration::from_secs(1));
+    app.close();
 }
 
 fn main() {
index 1cdd1d89ef054fe418398b15fd9866a765421ae1..012cc4612d4dcbb7737710a7baf5374dad26a6e0 100755 (executable)
@@ -1,18 +1,14 @@
 use m17app::adapter::PacketAdapter;
 use m17app::app::M17App;
 use m17app::link_setup::LinkSetup;
-use m17app::soundmodem::{InputRrcFile, NullOutputSink, NullPtt, Soundmodem};
-use m17core::protocol::PacketType;
-use std::path::PathBuf;
+use m17app::soundcard::Soundcard;
+use m17app::soundmodem::{NullOutputSink, NullPtt, Soundmodem};
+use m17app::PacketType;
 use std::sync::Arc;
 
 fn main() {
-    let path = PathBuf::from("../../../Data/mypacket.rrc");
-    let soundmodem = Soundmodem::new(
-        InputRrcFile::new(path),
-        NullOutputSink::new(),
-        NullPtt::new(),
-    );
+    let soundcard = Soundcard::new("plughw:CARD=Device,DEV=0").unwrap();
+    let soundmodem = Soundmodem::new(soundcard.input(), NullOutputSink::new(), NullPtt::new());
     let app = M17App::new(soundmodem);
     app.add_packet_adapter(PacketPrinter);
     app.start();
diff --git a/tools/m17rt-soundcards/Cargo.toml b/tools/m17rt-soundcards/Cargo.toml
new file mode 100644 (file)
index 0000000..b8f7db5
--- /dev/null
@@ -0,0 +1,10 @@
+[package]
+name = "m17rt-soundcards"
+version = "0.1.0"
+edition = "2021"
+license = "MIT"
+authors = ["Thomas Karpiniec <tom.karpiniec@outlook.com"]
+publish = false
+
+[dependencies]
+m17app = { path = "../../m17app" }
diff --git a/tools/m17rt-soundcards/src/main.rs b/tools/m17rt-soundcards/src/main.rs
new file mode 100644 (file)
index 0000000..bc5b13d
--- /dev/null
@@ -0,0 +1,16 @@
+use m17app::soundcard::Soundcard;
+
+fn main() {
+    let inputs = Soundcard::supported_input_cards();
+    let outputs = Soundcard::supported_output_cards();
+
+    println!("\nSupported Input Soundcards ({}):\n", inputs.len());
+    for i in inputs {
+        println!("{}", i);
+    }
+
+    println!("\nSupported Output Soundcards ({}):\n", outputs.len());
+    for o in outputs {
+        println!("{}", o);
+    }
+}
index 538291aecfd55fa3a5043fb7c91c41b2fc6496dd..fb6eda19368dc61ea73934c383a0a5be9b181171 100644 (file)
@@ -1,33 +1,26 @@
 use m17app::app::M17App;
 use m17app::link_setup::{LinkSetup, M17Address};
-use m17app::soundmodem::{
-    InputRrcFile, InputSoundcard, NullInputSource, NullOutputSink, NullPtt, OutputRrcFile,
-    OutputSoundcard, Soundmodem,
-};
+use m17app::serial::{PttPin, SerialPtt};
+use m17app::soundcard::Soundcard;
+use m17app::soundmodem::Soundmodem;
 use m17core::protocol::PacketType;
-use std::path::PathBuf;
 
-pub fn mod_test() {
-    let out_path = PathBuf::from("../../../Data/mypacket.rrc");
-    let output = OutputRrcFile::new(out_path);
-    //let output = OutputSoundcard::new();
-    let soundmodem = Soundmodem::new(NullInputSource::new(), output, NullPtt::new());
+fn main() {
+    let soundcard = Soundcard::new("plughw:CARD=Device,DEV=0").unwrap();
+    let ptt = SerialPtt::new("/dev/ttyUSB0", PttPin::Rts);
+    let soundmodem = Soundmodem::new(soundcard.input(), soundcard.output(), ptt);
     let app = M17App::new(soundmodem);
+
     app.start();
-    std::thread::sleep(std::time::Duration::from_secs(1));
-    println!("Transmitting packet...");
 
-    let source = M17Address::from_callsign("VK7XT").unwrap();
+    println!("Transmitting packet...");
+    let source = M17Address::from_callsign("VK7XT-1").unwrap();
     let destination = M17Address::new_broadcast();
     let link_setup = LinkSetup::new_packet(&source, &destination);
     let payload = b"Hello, world!";
     app.tx()
-        .transmit_packet(&link_setup, PacketType::Raw, payload);
+        .transmit_packet(&link_setup, PacketType::Sms, payload);
 
-    std::thread::sleep(std::time::Duration::from_secs(5));
-}
-
-fn main() {
-    env_logger::init();
-    mod_test();
+    std::thread::sleep(std::time::Duration::from_secs(1));
+    app.close();
 }