Initial gui working
This commit is contained in:
9 files changed
+3615
-103
No files matched your search
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "tap_gui"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.27"
|
||||
env_logger = "0.11.8"
|
||||
tap_core = {path = "../tap_core"}
|
||||
cpal = "0.18.2"
|
||||
egui = "0.36.2"
|
||||
eframe = "0.36.2"
|
||||
tokio = { version = "1.53.2", features = ["rt", "rt-multi-thread", "macros"] }
|
||||
thiserror = "2.0.21"
|
||||
@@ -0,0 +1,146 @@
|
||||
use cpal::traits::{DeviceTrait, HostTrait};
|
||||
use eframe::{App, Frame};
|
||||
use egui::Ui;
|
||||
use env_logger::Env;
|
||||
use log::{error, info};
|
||||
use std::sync::mpsc::{Receiver, channel};
|
||||
use std::thread;
|
||||
use std::thread::JoinHandle;
|
||||
use tap_core::audio_source_thread::{AudioSourceThread, Commands};
|
||||
use tap_core::udp_connection::OpusUdpConnection;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum State {
|
||||
#[default]
|
||||
Init,
|
||||
Connecting,
|
||||
Connected,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ThreadError {
|
||||
#[error("Failed to establish udp connection: {0}")]
|
||||
IOError(#[from] std::io::Error),
|
||||
#[error("Audio thread error: {0}")]
|
||||
TapError(#[from] tap_core::audio_source_thread::Error),
|
||||
}
|
||||
|
||||
pub struct Tap {
|
||||
mode: State,
|
||||
device: String,
|
||||
denny_jack_host: String,
|
||||
warning: String,
|
||||
join_handle: Option<JoinHandle<Result<(), ThreadError>>>,
|
||||
channel: Option<std::sync::mpsc::Sender<Commands>>,
|
||||
host: cpal::Host,
|
||||
}
|
||||
|
||||
impl Tap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mode: Default::default(),
|
||||
device: "".to_string(),
|
||||
denny_jack_host: "".to_string(),
|
||||
warning: "".to_string(),
|
||||
join_handle: None,
|
||||
channel: None,
|
||||
host: cpal::default_host(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bg_threads(
|
||||
device: &str,
|
||||
host: &str,
|
||||
channel: Receiver<Commands>,
|
||||
) -> Result<(), ThreadError> {
|
||||
let udp_connection =
|
||||
match OpusUdpConnection::new("0.0.0.0:42069".parse().unwrap(), host.parse().unwrap()) {
|
||||
Ok(c) => c,
|
||||
Err(err) => {
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
||||
let host = cpal::default_host();
|
||||
let audio_source_thread = AudioSourceThread::new(device.to_string(), host, channel);
|
||||
|
||||
// Run until completion
|
||||
audio_source_thread.worker(udp_connection)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl App for Tap {
|
||||
fn ui(&mut self, ui: &mut Ui, frame: &mut Frame) {
|
||||
match self.mode {
|
||||
State::Init => {
|
||||
ui.label("Denny Jack Host");
|
||||
ui.text_edit_singleline(&mut self.denny_jack_host);
|
||||
egui::containers::ComboBox::from_label("Device:")
|
||||
.selected_text(&self.device)
|
||||
.show_ui(ui, |ui| {
|
||||
for device in self.host.devices().unwrap() {
|
||||
ui.selectable_value(
|
||||
&mut self.device,
|
||||
device.id().unwrap().to_string(),
|
||||
device.to_string(),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if ui.button("Connect").clicked() {
|
||||
if self.device.is_empty() || self.denny_jack_host.is_empty() {
|
||||
self.warning = "Enter connection details and try again".to_string();
|
||||
} else {
|
||||
self.warning = "".to_string();
|
||||
self.mode = State::Connecting;
|
||||
}
|
||||
}
|
||||
|
||||
if !self.warning.is_empty() {
|
||||
ui.label(format!("Error: {}", &self.warning));
|
||||
}
|
||||
}
|
||||
State::Connecting => {
|
||||
ui.label("Connecting");
|
||||
let device = self.device.to_string();
|
||||
let host = self.denny_jack_host.to_string();
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
|
||||
let join = thread::spawn(move || Self::bg_threads(&device, &host, receiver));
|
||||
|
||||
self.join_handle = Some(join);
|
||||
self.channel = Some(sender);
|
||||
self.mode = State::Connected;
|
||||
}
|
||||
State::Connected => {
|
||||
ui.label("Connected");
|
||||
|
||||
if self.join_handle.as_ref().unwrap().is_finished() {
|
||||
let join = self.join_handle.take().unwrap();
|
||||
let err = join.join();
|
||||
error!("Thread joined early: {err:?}");
|
||||
|
||||
self.warning = "Disconnected...".to_string();
|
||||
|
||||
self.channel = None;
|
||||
self.join_handle = None;
|
||||
self.mode = State::Init;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), eframe::Error> {
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
||||
let options = eframe::NativeOptions {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
eframe::run_native("Tap", options, Box::new(|cc| Ok(Box::new(Tap::new()))))
|
||||
}
|
||||
Reference in new issue
Block a user