feat: ✨ talk and listen on client side
This commit is contained in:
parent
1567a9c32a
commit
1e9808579a
11 changed files with 388 additions and 79 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -1502,7 +1502,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"presser",
|
"presser",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"windows 0.58.0",
|
"windows 0.54.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3133,7 +3133,7 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.9.4",
|
"linux-raw-sys 0.9.4",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3731,7 +3731,7 @@ dependencies = [
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix 1.0.7",
|
"rustix 1.0.7",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -10,5 +10,5 @@ serde_json = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
s2n-quic = { workspace = true }
|
s2n-quic = { workspace = true }
|
||||||
iced = { features = ["tokio"], git = "https://github.com/iced-rs/iced", rev = "d39022432c778a8cda455f40b9c12245db86ce45" }
|
|
||||||
cpal = "0.15.3"
|
cpal = "0.15.3"
|
||||||
|
iced = { features = ["tokio"], git = "https://github.com/iced-rs/iced", rev = "d39022432c778a8cda455f40b9c12245db86ce45" }
|
||||||
|
|
34
client/certificates/cert.pem
Normal file
34
client/certificates/cert.pem
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIF1zCCA7+gAwIBAgIUebuL+Py/iYfh5YJh8MeAq01P/7MwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwezELMAkGA1UEBhMCWFgxEjAQBgNVBAgMCVN0YXRlTmFtZTERMA8GA1UEBwwI
|
||||||
|
Q2l0eU5hbWUxFDASBgNVBAoMC0NvbXBhbnlOYW1lMRswGQYDVQQLDBJDb21wYW55
|
||||||
|
U2VjdGlvbk5hbWUxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yNTAzMDEwMDAyMDha
|
||||||
|
Fw0zNTAyMjcwMDAyMDhaMHsxCzAJBgNVBAYTAlhYMRIwEAYDVQQIDAlTdGF0ZU5h
|
||||||
|
bWUxETAPBgNVBAcMCENpdHlOYW1lMRQwEgYDVQQKDAtDb21wYW55TmFtZTEbMBkG
|
||||||
|
A1UECwwSQ29tcGFueVNlY3Rpb25OYW1lMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIi
|
||||||
|
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDuCGUWsnP5KNBLNcPN3npHpt6y
|
||||||
|
VKX37w3Qd190oyxOBLzMLDBb3QZ9JqmFAghhhupYV/e9xRWCHUbNifHZxfkf2uSR
|
||||||
|
1bdfi6KcPU7uHqF6+zmPp6SQ1tYN+iCqEeprlzw3dnnSvvWbe3DSWmT8oXJwXZtG
|
||||||
|
ExY0QNRky23QBpcAUcTAO/JMGRvimxqNPHRUHxQibLUefZjoXQg5CGoREhubPkGy
|
||||||
|
Y1qPSTPR6khcSitQ006TVnpEo4YjDDwhP+oGAMBP0eAn6tfLWDGHlOfRxqW8a3EX
|
||||||
|
FL2J0ZwthJlP8lJnrYur9f2GELruhgD6vebIZQDF6/0yoZ07hc3wA5V+FxwgzcyK
|
||||||
|
MOvf7zAQQwL5Cj1VnN6t4mbhjoJOat3mgLFE87PGr6zGlqeovFOE2k5IuDCzd6VN
|
||||||
|
7UK/F36zxBVfrGVbE9FqdjdVU2OR2tBY3+De2DvBnHI6PXH1rC51pJytMjhACGNu
|
||||||
|
txsOaQiU3G2RzUqaxI7NqLyoL6mAfuzDvtDz9WKbf6rXFncRla8fbTmZ3C6hZh0l
|
||||||
|
IIb9wk6DWALL4n1l2wF1Lpe+gWgY1QVq8alZX9qvCVwHQr24URXv23HQgAPaTpv4
|
||||||
|
c/b7eCymHLYp9CHGtgXKwlFj1Msju26OMVZ13qugOR2eF/OmsaonLVt10jX5y+VW
|
||||||
|
FUS9QjynjRqKw0yPBwIDAQABo1MwUTAdBgNVHQ4EFgQUcPQsBRhlo9zFSSFw1Mgz
|
||||||
|
COo8Ll4wHwYDVR0jBBgwFoAUcPQsBRhlo9zFSSFw1MgzCOo8Ll4wDwYDVR0TAQH/
|
||||||
|
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAY3724fnTOLR8eMIP06+lF9p18DD+
|
||||||
|
Or10CgaGyamCofkrA84bbN+gGdVBWlk3V2c1E9C4yna2iqQUwf8JJIzTdEZbmOU1
|
||||||
|
vwLu7YCGMgISOwUEEwXzdLOsQg2AEmfpI2nru5pUZFrvrBbDwfkyWckv5ZjNiHlB
|
||||||
|
nTmgatcGHTY2BKzQJzLHuo8HimFrfMXDMatb8K0OdujSYFuQ6Hk4L9NK0nx0art4
|
||||||
|
3t5IIXg0R34mpghz+lm/FEJdKUJYsW/Czb48ro2pb1DL673PmfJ4qQC31lINiaRO
|
||||||
|
AWrfjWOsELGEFzHbFnMukUn5C7HMNnu3XZ8X/d0zxELUkKDKGXC42mWRDjWkklB/
|
||||||
|
UhLHgy9xBmv8cQwZ+6IHppAT0wb+yYPP+xfkXeRQTmz7SDVIInm8mZxHDQbGcFsw
|
||||||
|
caXgF1U4KiMKXglq/gU7Y7zMJzLxxCiSgdngY2baP7ivO7Oh3kjvO9NmLIlk2fHE
|
||||||
|
qztnQS3CX3hIE5MKN2JKeY20HMALz73XpfzPkACt6/9iIUeHKoHJrL9pP+2mI18l
|
||||||
|
KAJUDgBgyxtMg8UiFihpeBrzqiv+JxP9v3LPKaJhITNOS7fQUWBH0YM2Y2uWm+qh
|
||||||
|
GQfCL1GS/QZINf5bkDgcM6R2sACXL+ysCY9b+xl1oWnIY21TbmbughxLNsb0SVEJ
|
||||||
|
YMPR5Ge743qMQLg=
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -1,42 +1,172 @@
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use iced::{
|
use iced::{
|
||||||
Alignment::Center,
|
Alignment::Center,
|
||||||
Element, Task, Theme,
|
Element, Task, Theme,
|
||||||
widget::{button, column, text},
|
widget::{button, column, row},
|
||||||
|
};
|
||||||
|
use protocol::BUFFER_LENGTH;
|
||||||
|
use tokio::sync::{
|
||||||
|
broadcast::{self},
|
||||||
|
oneshot,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{ClientConfig, stream::connect};
|
use crate::{ClientConfig, stream::connect, voice::record};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Message {
|
|
||||||
JoinRoom,
|
|
||||||
LeaveRoom,
|
|
||||||
MuteMicrophone,
|
|
||||||
UnmuteMicrophone,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Data {
|
struct Signal {
|
||||||
client_config: Option<ClientConfig>,
|
microphone: Option<oneshot::Sender<bool>>,
|
||||||
|
// speaker: Option<oneshot::Sender<bool>>,
|
||||||
|
connection: Option<oneshot::Sender<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
#[derive(Debug)]
|
||||||
pub fn update(&mut self, message: Message) -> Task<Message> {
|
struct Channel {
|
||||||
match message {
|
microphone: broadcast::Sender<f32>,
|
||||||
Message::JoinRoom => Task::none(),
|
// speaker: (broadcast::Sender<f32>, broadcast::Receiver<f32>),
|
||||||
Message::LeaveRoom => Task::none(),
|
}
|
||||||
Message::MuteMicrophone => Task::none(),
|
|
||||||
Message::UnmuteMicrophone => Task::none(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn view(&self) -> Element<'_, Message> {
|
impl Channel {
|
||||||
column![button("Join Room"), text("Hello").size(50)]
|
fn new() -> Self {
|
||||||
.padding(10)
|
Self {
|
||||||
.align_x(Center)
|
microphone: broadcast::channel(BUFFER_LENGTH).0,
|
||||||
.into()
|
// speaker: broadcast::channel(BUFFER_LENGTH),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
struct GUIStatus {
|
||||||
|
room: State,
|
||||||
|
microphone: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum State {
|
||||||
|
Active,
|
||||||
|
Passive,
|
||||||
|
Loading,
|
||||||
|
}
|
||||||
|
impl Default for State {
|
||||||
|
fn default() -> Self {
|
||||||
|
State::Passive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum Message {
|
||||||
|
State,
|
||||||
|
JoinRoom,
|
||||||
|
LeaveRoom,
|
||||||
|
UnmuteMicrophone,
|
||||||
|
MuteMicrophone,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct App {
|
||||||
|
client_config: Arc<ClientConfig>,
|
||||||
|
gui_status: Arc<RwLock<GUIStatus>>,
|
||||||
|
channel: Channel,
|
||||||
|
signal: Signal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
pub fn theme(&self) -> Theme {
|
pub fn theme(&self) -> Theme {
|
||||||
Theme::Dark
|
Theme::Dark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
App {
|
||||||
|
client_config: ClientConfig::new().into(),
|
||||||
|
gui_status: RwLock::new(GUIStatus::default()).into(),
|
||||||
|
channel: Channel::new(),
|
||||||
|
signal: Signal::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn view(&self) -> Element<'_, Message> {
|
||||||
|
let join_room_button = match self.gui_status.read().unwrap().room {
|
||||||
|
State::Active => button("Leave Room").on_press(Message::LeaveRoom),
|
||||||
|
State::Passive => button("Join Room").on_press(Message::JoinRoom),
|
||||||
|
State::Loading => button("Loading"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let microphone_button = match self.gui_status.read().unwrap().microphone {
|
||||||
|
State::Active => button("Mute").on_press(Message::MuteMicrophone),
|
||||||
|
State::Passive => button("Unmute").on_press(Message::UnmuteMicrophone),
|
||||||
|
State::Loading => button("Loading"),
|
||||||
|
};
|
||||||
|
column![
|
||||||
|
row![join_room_button, microphone_button]
|
||||||
|
.spacing(20)
|
||||||
|
.align_y(Center)
|
||||||
|
]
|
||||||
|
.align_x(Center)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||||
|
match message {
|
||||||
|
Message::State => Task::none(),
|
||||||
|
Message::JoinRoom => {
|
||||||
|
self.gui_status.write().unwrap().room = State::Loading;
|
||||||
|
let client_config = self.client_config.clone();
|
||||||
|
let gui_status = self.gui_status.clone();
|
||||||
|
let microphone_receiver = self.channel.microphone.subscribe();
|
||||||
|
|
||||||
|
let connection_signal = oneshot::channel();
|
||||||
|
self.signal.connection = Some(connection_signal.0);
|
||||||
|
Task::perform(
|
||||||
|
connect(connection_signal.1, microphone_receiver, client_config),
|
||||||
|
move |result| match result {
|
||||||
|
Ok(_) => gui_status.write().unwrap().room = State::Active,
|
||||||
|
Err(err_val) => {
|
||||||
|
eprintln!("Error: Join Room | {}", err_val);
|
||||||
|
gui_status.write().unwrap().room = State::Passive;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map(|_| Message::State)
|
||||||
|
}
|
||||||
|
Message::LeaveRoom => {
|
||||||
|
self.gui_status.write().unwrap().room = State::Loading;
|
||||||
|
if let Some(connection_signal) = &self.signal.connection {
|
||||||
|
if !connection_signal.is_closed() {
|
||||||
|
self.signal
|
||||||
|
.connection
|
||||||
|
.take()
|
||||||
|
.expect("Never")
|
||||||
|
.send(true)
|
||||||
|
.unwrap();
|
||||||
|
self.signal.connection = None;
|
||||||
|
self.gui_status.write().unwrap().room = State::Passive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::UnmuteMicrophone => {
|
||||||
|
self.gui_status.write().unwrap().microphone = State::Active;
|
||||||
|
let microphone_sender = self.channel.microphone.clone();
|
||||||
|
let microphone_stop_signal = oneshot::channel();
|
||||||
|
self.signal.microphone = Some(microphone_stop_signal.0);
|
||||||
|
Task::perform(record(microphone_sender, microphone_stop_signal.1), |_| {
|
||||||
|
Message::State
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Message::MuteMicrophone => {
|
||||||
|
self.gui_status.write().unwrap().microphone = State::Loading;
|
||||||
|
if let Some(microphone_signal) = &self.signal.microphone {
|
||||||
|
if !microphone_signal.is_closed() {
|
||||||
|
self.signal
|
||||||
|
.microphone
|
||||||
|
.take()
|
||||||
|
.expect("Never")
|
||||||
|
.send(true)
|
||||||
|
.unwrap();
|
||||||
|
self.signal.microphone = None;
|
||||||
|
self.gui_status.write().unwrap().microphone = State::Passive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,16 @@ pub mod stream;
|
||||||
pub mod voice;
|
pub mod voice;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ClientConfig {
|
pub struct ClientConfig {
|
||||||
|
certificate_path: String,
|
||||||
server_address: String,
|
server_address: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ClientConfig {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
certificate_path: "./client/certificates/cert.pem".to_string(),
|
||||||
|
server_address: "localhost:4546".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use client::gui::Data;
|
use client::gui::App;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
|
iced::application(App::new, App::update, App::view)
|
||||||
iced::application(Data::default, Data::update, Data::view)
|
.title("Tahinli Communication")
|
||||||
.theme(Data::theme)
|
.theme(App::theme)
|
||||||
|
.centered()
|
||||||
.run()
|
.run()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +1,84 @@
|
||||||
use std::{io, net::SocketAddr, path::Path};
|
use std::{net::SocketAddr, path::Path, sync::Arc};
|
||||||
|
|
||||||
use protocol::BUFFER_LENGTH;
|
use protocol::{BUFFER_LENGTH, Error};
|
||||||
use s2n_quic::{Client, client::Connect};
|
use s2n_quic::{Client, client::Connect};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::AsyncReadExt,
|
io::AsyncReadExt,
|
||||||
sync::{broadcast, oneshot},
|
sync::{broadcast, oneshot},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{ClientConfig, voice::play};
|
||||||
ClientConfig,
|
|
||||||
voice::{play, record},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub async fn connect(client_config: &ClientConfig) {
|
pub async fn connect(
|
||||||
|
connection_signal: oneshot::Receiver<bool>,
|
||||||
|
mut microphone_receiver: broadcast::Receiver<f32>,
|
||||||
|
client_config: Arc<ClientConfig>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let client = Client::builder()
|
let client = Client::builder()
|
||||||
.with_io("0:0")
|
.with_io("0:0")
|
||||||
.unwrap()
|
.map_err(|err_val| Error::ConnectionSetup(err_val.to_string()))?
|
||||||
.with_tls(Path::new("certificates/cert.pem"))
|
.with_tls(Path::new(&client_config.certificate_path))
|
||||||
.unwrap()
|
.map_err(|err_val| Error::ConnectionSetup(err_val.to_string()))?
|
||||||
.start()
|
.start()
|
||||||
.unwrap();
|
.map_err(|err_val| Error::ConnectionSetup(err_val.to_string()))?;
|
||||||
|
|
||||||
println!("Client Address = {}", client.local_addr().unwrap());
|
println!("Client Address = {}", client.local_addr().unwrap());
|
||||||
let connect = Connect::new(client_config.server_address.parse::<SocketAddr>().unwrap())
|
let connect = Connect::new(
|
||||||
|
client_config
|
||||||
|
.server_address
|
||||||
|
.parse::<SocketAddr>()
|
||||||
|
.map_err(|inner| Error::Connection(inner.to_string()))?,
|
||||||
|
)
|
||||||
.with_server_name("localhost");
|
.with_server_name("localhost");
|
||||||
let mut connection = match client.connect(connect).await {
|
let mut connection = match client.connect(connect).await {
|
||||||
Ok(connection) => connection,
|
Ok(connection) => connection,
|
||||||
Err(err_val) => {
|
Err(err_val) => {
|
||||||
eprintln!("Error: Client Connection | {}", err_val);
|
eprintln!("Error: Client Connection | {}", err_val);
|
||||||
return;
|
return Err(Error::Connection(err_val.to_string()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
connection.keep_alive(true).unwrap();
|
connection
|
||||||
|
.keep_alive(true)
|
||||||
|
.map_err(|err_val| Error::ConnectionSetup(err_val.to_string()))?;
|
||||||
|
|
||||||
let stream = connection.open_bidirectional_stream().await.unwrap();
|
let stream = connection
|
||||||
|
.open_bidirectional_stream()
|
||||||
|
.await
|
||||||
|
.map_err(|err_val| Error::ConnectionSetup(err_val.to_string()))?;
|
||||||
let (mut receive_stream, mut send_stream) = stream.split();
|
let (mut receive_stream, mut send_stream) = stream.split();
|
||||||
|
|
||||||
let (microphone_sender, mut microphone_receiver) = broadcast::channel::<f32>(BUFFER_LENGTH);
|
|
||||||
let (speaker_sender, speaker_receiver) = broadcast::channel::<f32>(BUFFER_LENGTH);
|
let (speaker_sender, speaker_receiver) = broadcast::channel::<f32>(BUFFER_LENGTH);
|
||||||
|
|
||||||
let (microphone_stop_signal_sender, microphone_stop_signal_receiver) =
|
|
||||||
oneshot::channel::<bool>();
|
|
||||||
let (spearker_stop_signal_sender, speaker_stop_signal_receiver) = oneshot::channel::<bool>();
|
let (spearker_stop_signal_sender, speaker_stop_signal_receiver) = oneshot::channel::<bool>();
|
||||||
|
|
||||||
tokio::spawn(play(speaker_receiver, speaker_stop_signal_receiver));
|
let play_task = tokio::spawn(play(speaker_receiver, speaker_stop_signal_receiver));
|
||||||
tokio::spawn(record(microphone_sender, microphone_stop_signal_receiver));
|
let receive_task = tokio::spawn(async move {
|
||||||
tokio::spawn(async move {
|
|
||||||
while let Ok(data) = receive_stream.read_f32_le().await {
|
while let Ok(data) = receive_stream.read_f32_le().await {
|
||||||
speaker_sender.send(data).unwrap();
|
speaker_sender
|
||||||
|
.send(data)
|
||||||
|
.map_err(|err_val| Error::Signal(err_val.to_string()))
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
tokio::spawn(async move {
|
let send_task = tokio::spawn(async move {
|
||||||
while let Ok(data) = microphone_receiver.recv().await {
|
while let Ok(data) = microphone_receiver.recv().await {
|
||||||
send_stream
|
send_stream
|
||||||
.send(data.to_le_bytes().to_vec().into())
|
.send(data.to_le_bytes().to_vec().into())
|
||||||
.await
|
.await
|
||||||
|
.map_err(|err_val| Error::Send(err_val.to_string()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let mut read_buffer = String::default();
|
if let Ok(_) = connection_signal.await {
|
||||||
io::stdin().read_line(&mut read_buffer).unwrap();
|
println!("Connection Closing");
|
||||||
microphone_stop_signal_sender.send(true).unwrap();
|
}
|
||||||
spearker_stop_signal_sender.send(true).unwrap();
|
spearker_stop_signal_sender
|
||||||
|
.send(true)
|
||||||
|
.map_err(|err_val| Error::Signal(err_val.to_string()))?;
|
||||||
|
|
||||||
|
send_task.abort();
|
||||||
|
receive_task.abort();
|
||||||
|
play_task.abort();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use tokio::sync::{broadcast, oneshot};
|
||||||
|
|
||||||
pub async fn record(
|
pub async fn record(
|
||||||
microphone_sender: broadcast::Sender<f32>,
|
microphone_sender: broadcast::Sender<f32>,
|
||||||
stop_signal_receiver: oneshot::Receiver<bool>,
|
microphone_stop_signal: oneshot::Receiver<bool>,
|
||||||
) {
|
) {
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
let input_device = host.default_input_device().unwrap();
|
let input_device = host.default_input_device().unwrap();
|
||||||
|
@ -26,7 +26,7 @@ pub async fn record(
|
||||||
println!("Recording Started");
|
println!("Recording Started");
|
||||||
|
|
||||||
tokio::task::block_in_place(|| {
|
tokio::task::block_in_place(|| {
|
||||||
let _ = stop_signal_receiver.blocking_recv();
|
let _ = microphone_stop_signal.blocking_recv();
|
||||||
});
|
});
|
||||||
|
|
||||||
input_stream.pause().unwrap();
|
input_stream.pause().unwrap();
|
||||||
|
@ -34,7 +34,7 @@ pub async fn record(
|
||||||
|
|
||||||
pub async fn play(
|
pub async fn play(
|
||||||
mut speaker_receiver: broadcast::Receiver<f32>,
|
mut speaker_receiver: broadcast::Receiver<f32>,
|
||||||
stop_signal_receiver: oneshot::Receiver<bool>,
|
speaker_stop_signal: oneshot::Receiver<bool>,
|
||||||
) {
|
) {
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
let output_device = host.default_output_device().unwrap();
|
let output_device = host.default_output_device().unwrap();
|
||||||
|
@ -61,7 +61,7 @@ pub async fn play(
|
||||||
println!("Playing Started");
|
println!("Playing Started");
|
||||||
|
|
||||||
tokio::task::block_in_place(|| {
|
tokio::task::block_in_place(|| {
|
||||||
let _ = stop_signal_receiver.blocking_recv();
|
let _ = speaker_stop_signal.blocking_recv();
|
||||||
});
|
});
|
||||||
|
|
||||||
output_stream.pause().unwrap();
|
output_stream.pause().unwrap();
|
||||||
|
|
|
@ -1,11 +1,45 @@
|
||||||
use std::{collections::VecDeque, fs::File, io::Read};
|
use std::{
|
||||||
|
collections::{HashMap, VecDeque},
|
||||||
|
fmt::Display,
|
||||||
|
fs::File,
|
||||||
|
io::Read,
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub const BUFFER_LENGTH: usize = 1024;
|
pub const BUFFER_LENGTH: usize = 1024;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum Error {
|
||||||
|
ConnectionSetup(String),
|
||||||
|
Connection(String),
|
||||||
|
Send(String),
|
||||||
|
Receive(String),
|
||||||
|
Signal(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn cause(&self) -> Option<&dyn std::error::Error> {
|
||||||
|
self.source()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::ConnectionSetup(inner) => write!(f, "Connection Setup | {}", inner),
|
||||||
|
Error::Connection(inner) => write!(f, "Connection | {}", inner),
|
||||||
|
Error::Send(inner) => write!(f, "Send | {}", inner),
|
||||||
|
Error::Receive(inner) => write!(f, "Receive | {}", inner),
|
||||||
|
Error::Signal(inner) => write!(f, "Signal | {}", inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct TOML {
|
struct TOML {
|
||||||
header: String,
|
header: String,
|
||||||
fields: VecDeque<String>,
|
fields: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn naive_toml_parser(file_location: &str) -> TOML {
|
fn naive_toml_parser(file_location: &str) -> TOML {
|
||||||
|
@ -21,19 +55,14 @@ fn naive_toml_parser(file_location: &str) -> TOML {
|
||||||
.replace(']', "")
|
.replace(']', "")
|
||||||
.trim_end()
|
.trim_end()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
let mut fields = HashMap::new();
|
||||||
let fields = toml_ingredients
|
let _ = toml_ingredients.iter().map(|ingredient| {
|
||||||
.iter()
|
ingredient.split_once('=').map(|(key, value)| {
|
||||||
.map(|ingredient| {
|
let key = key.replace('"', "").trim().to_string();
|
||||||
ingredient
|
let value = value.replace('"', "").trim().to_string();
|
||||||
.split_once('=')
|
fields.insert(key, value);
|
||||||
.unwrap()
|
|
||||||
.1
|
|
||||||
.replace('"', "")
|
|
||||||
.trim()
|
|
||||||
.to_string()
|
|
||||||
})
|
})
|
||||||
.collect();
|
});
|
||||||
|
|
||||||
TOML { header, fields }
|
TOML { header, fields }
|
||||||
}
|
}
|
||||||
|
|
34
server/certificates/cert.pem
Normal file
34
server/certificates/cert.pem
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIF1zCCA7+gAwIBAgIUebuL+Py/iYfh5YJh8MeAq01P/7MwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwezELMAkGA1UEBhMCWFgxEjAQBgNVBAgMCVN0YXRlTmFtZTERMA8GA1UEBwwI
|
||||||
|
Q2l0eU5hbWUxFDASBgNVBAoMC0NvbXBhbnlOYW1lMRswGQYDVQQLDBJDb21wYW55
|
||||||
|
U2VjdGlvbk5hbWUxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yNTAzMDEwMDAyMDha
|
||||||
|
Fw0zNTAyMjcwMDAyMDhaMHsxCzAJBgNVBAYTAlhYMRIwEAYDVQQIDAlTdGF0ZU5h
|
||||||
|
bWUxETAPBgNVBAcMCENpdHlOYW1lMRQwEgYDVQQKDAtDb21wYW55TmFtZTEbMBkG
|
||||||
|
A1UECwwSQ29tcGFueVNlY3Rpb25OYW1lMRIwEAYDVQQDDAlsb2NhbGhvc3QwggIi
|
||||||
|
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDuCGUWsnP5KNBLNcPN3npHpt6y
|
||||||
|
VKX37w3Qd190oyxOBLzMLDBb3QZ9JqmFAghhhupYV/e9xRWCHUbNifHZxfkf2uSR
|
||||||
|
1bdfi6KcPU7uHqF6+zmPp6SQ1tYN+iCqEeprlzw3dnnSvvWbe3DSWmT8oXJwXZtG
|
||||||
|
ExY0QNRky23QBpcAUcTAO/JMGRvimxqNPHRUHxQibLUefZjoXQg5CGoREhubPkGy
|
||||||
|
Y1qPSTPR6khcSitQ006TVnpEo4YjDDwhP+oGAMBP0eAn6tfLWDGHlOfRxqW8a3EX
|
||||||
|
FL2J0ZwthJlP8lJnrYur9f2GELruhgD6vebIZQDF6/0yoZ07hc3wA5V+FxwgzcyK
|
||||||
|
MOvf7zAQQwL5Cj1VnN6t4mbhjoJOat3mgLFE87PGr6zGlqeovFOE2k5IuDCzd6VN
|
||||||
|
7UK/F36zxBVfrGVbE9FqdjdVU2OR2tBY3+De2DvBnHI6PXH1rC51pJytMjhACGNu
|
||||||
|
txsOaQiU3G2RzUqaxI7NqLyoL6mAfuzDvtDz9WKbf6rXFncRla8fbTmZ3C6hZh0l
|
||||||
|
IIb9wk6DWALL4n1l2wF1Lpe+gWgY1QVq8alZX9qvCVwHQr24URXv23HQgAPaTpv4
|
||||||
|
c/b7eCymHLYp9CHGtgXKwlFj1Msju26OMVZ13qugOR2eF/OmsaonLVt10jX5y+VW
|
||||||
|
FUS9QjynjRqKw0yPBwIDAQABo1MwUTAdBgNVHQ4EFgQUcPQsBRhlo9zFSSFw1Mgz
|
||||||
|
COo8Ll4wHwYDVR0jBBgwFoAUcPQsBRhlo9zFSSFw1MgzCOo8Ll4wDwYDVR0TAQH/
|
||||||
|
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAY3724fnTOLR8eMIP06+lF9p18DD+
|
||||||
|
Or10CgaGyamCofkrA84bbN+gGdVBWlk3V2c1E9C4yna2iqQUwf8JJIzTdEZbmOU1
|
||||||
|
vwLu7YCGMgISOwUEEwXzdLOsQg2AEmfpI2nru5pUZFrvrBbDwfkyWckv5ZjNiHlB
|
||||||
|
nTmgatcGHTY2BKzQJzLHuo8HimFrfMXDMatb8K0OdujSYFuQ6Hk4L9NK0nx0art4
|
||||||
|
3t5IIXg0R34mpghz+lm/FEJdKUJYsW/Czb48ro2pb1DL673PmfJ4qQC31lINiaRO
|
||||||
|
AWrfjWOsELGEFzHbFnMukUn5C7HMNnu3XZ8X/d0zxELUkKDKGXC42mWRDjWkklB/
|
||||||
|
UhLHgy9xBmv8cQwZ+6IHppAT0wb+yYPP+xfkXeRQTmz7SDVIInm8mZxHDQbGcFsw
|
||||||
|
caXgF1U4KiMKXglq/gU7Y7zMJzLxxCiSgdngY2baP7ivO7Oh3kjvO9NmLIlk2fHE
|
||||||
|
qztnQS3CX3hIE5MKN2JKeY20HMALz73XpfzPkACt6/9iIUeHKoHJrL9pP+2mI18l
|
||||||
|
KAJUDgBgyxtMg8UiFihpeBrzqiv+JxP9v3LPKaJhITNOS7fQUWBH0YM2Y2uWm+qh
|
||||||
|
GQfCL1GS/QZINf5bkDgcM6R2sACXL+ysCY9b+xl1oWnIY21TbmbughxLNsb0SVEJ
|
||||||
|
YMPR5Ge743qMQLg=
|
||||||
|
-----END CERTIFICATE-----
|
52
server/certificates/key.pem
Normal file
52
server/certificates/key.pem
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
OMQMuXIQAe6ADrQYo1TtBAwZwM7Jtf4=
|
||||||
|
-----END PRIVATE KEY-----
|
Loading…
Add table
Add a link
Reference in a new issue