aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--PKGBUILD31
-rw-r--r--mumctl/src/main.rs33
-rw-r--r--mumd/src/audio.rs25
-rw-r--r--mumd/src/network/tcp.rs1
-rw-r--r--mumd/src/state.rs50
-rw-r--r--mumlib/src/command.rs3
-rw-r--r--mumlib/src/error.rs24
-rw-r--r--usage.org8
8 files changed, 115 insertions, 60 deletions
diff --git a/PKGBUILD b/PKGBUILD
deleted file mode 100644
index fd586f7..0000000
--- a/PKGBUILD
+++ /dev/null
@@ -1,31 +0,0 @@
-# Maintainer: Eskil Queseth <eskilq at kth dot se>
-# Maintainer: Gustav Sörnäs <gustav at sornas dot net>
-
-pkgname=mum-git
-pkgver=0.1.0
-pkgrel=1
-pkgdesc="A mumble client/daemon pair"
-arch=('x86_64')
-url="https://github.com/sornas/mum.git"
-license=('MIT')
-sha256sums=('SKIP')
-depends=('alsa-lib' 'opus' 'openssl')
-makedepends=('git' 'rust')
-source=("git+$url")
-
-build() {
- cd "${srcdir}/${pkgname%-git}"
- cargo build --release --target-dir=target
-}
-
-check() {
- cd "${srcdir}/${pkgname%-git}"
- cargo test --release --target-dir=target
-}
-
-package() {
- cd "${srcdir}/${pkgname%-git}"
- install -Dm 755 target/release/mumctl -t "${pkgdir}/usr/bin"
- install -Dm 755 target/release/mumd -t "${pkgdir}/usr/bin"
- install -Dm 644 LICENSE -t "${pkgdir}/usr/share/licenses/${pkgname}"
-}
diff --git a/mumctl/src/main.rs b/mumctl/src/main.rs
index bcb5e71..2473195 100644
--- a/mumctl/src/main.rs
+++ b/mumctl/src/main.rs
@@ -44,12 +44,18 @@ fn main() {
),
)
.subcommand(SubCommand::with_name("status"))
- .subcommand(
- SubCommand::with_name("completions")
- .arg(Arg::with_name("zsh").long("zsh"))
- .arg(Arg::with_name("bash").long("bash"))
- .arg(Arg::with_name("fish").long("fish")),
- );
+ .subcommand(SubCommand::with_name("config")
+ .arg(Arg::with_name("name")
+ .required(true))
+ .arg(Arg::with_name("value")
+ .required(true)))
+ .subcommand(SubCommand::with_name("completions")
+ .arg(Arg::with_name("zsh")
+ .long("zsh"))
+ .arg(Arg::with_name("bash")
+ .long("bash"))
+ .arg(Arg::with_name("fish")
+ .long("fish")));
let matches = app.clone().get_matches();
@@ -79,7 +85,7 @@ fn main() {
}
} else if let Some(matches) = matches.subcommand_matches("connect") {
err_print!(send_command(Command::ChannelJoin {
- channel_id: matches.value_of("channel").unwrap().parse::<u32>().unwrap()
+ channel_identifier: matches.value_of("channel").unwrap().to_string()
}));
}
} else if let Some(_matches) = matches.subcommand_matches("status") {
@@ -114,6 +120,19 @@ fn main() {
},
Err(e) => println!("{} {}", "error:".red(), e),
}
+ } else if let Some(matches) = matches.subcommand_matches("config") {
+ let name = matches.value_of("name").unwrap();
+ let value = matches.value_of("value").unwrap();
+ match name {
+ "audio.input_volume" => {
+ if let Ok(volume) = value.parse() {
+ send_command(Command::InputVolumeSet(volume)).unwrap();
+ }
+ },
+ _ => {
+ println!("{} Unknown config value {}", "error:".red(), name);
+ }
+ }
} else if let Some(matches) = matches.subcommand_matches("completions") {
app.gen_completions_to(
"mumctl",
diff --git a/mumd/src/audio.rs b/mumd/src/audio.rs
index edc2f7f..828942b 100644
--- a/mumd/src/audio.rs
+++ b/mumd/src/audio.rs
@@ -13,13 +13,13 @@ use std::ops::AddAssign;
use std::sync::Arc;
use std::sync::Mutex;
use tokio::sync::mpsc::{self, Receiver, Sender};
+use tokio::sync::watch;
struct ClientStream {
buffer: VecDeque<f32>, //TODO ring buffer?
opus_decoder: opus::Decoder,
}
-//TODO remove pub where possible
pub struct Audio {
pub output_config: StreamConfig,
pub output_stream: Stream,
@@ -27,12 +27,12 @@ pub struct Audio {
pub input_config: StreamConfig,
pub input_stream: Stream,
pub input_buffer: Arc<Mutex<VecDeque<f32>>>,
- input_channel_receiver: Option<Receiver<VoicePacketPayload>>, //TODO unbounded? mbe ring buffer and drop the first packet
+ input_channel_receiver: Option<Receiver<VoicePacketPayload>>,
+ input_volume_sender: watch::Sender<f32>,
- client_streams: Arc<Mutex<HashMap<u32, ClientStream>>>, //TODO move to user state
+ client_streams: Arc<Mutex<HashMap<u32, ClientStream>>>,
}
-//TODO split into input/output
impl Audio {
pub fn new() -> Self {
let host = cpal::default_host();
@@ -99,6 +99,8 @@ impl Audio {
.unwrap();
let (input_sender, input_receiver) = mpsc::channel(100);
+ let (input_volume_sender, input_volume_receiver) = watch::channel::<f32>(1.0);
+
let input_buffer = Arc::new(Mutex::new(VecDeque::new()));
let input_stream = match input_supported_sample_format {
SampleFormat::F32 => input_device.build_input_stream(
@@ -107,6 +109,7 @@ impl Audio {
input_encoder,
input_sender,
input_config.sample_rate.0,
+ input_volume_receiver.clone(),
4, // 10 ms
),
err_fn,
@@ -117,6 +120,7 @@ impl Audio {
input_encoder,
input_sender,
input_config.sample_rate.0,
+ input_volume_receiver.clone(),
4, // 10 ms
),
err_fn,
@@ -127,6 +131,7 @@ impl Audio {
input_encoder,
input_sender,
input_config.sample_rate.0,
+ input_volume_receiver.clone(),
4, // 10 ms
),
err_fn,
@@ -142,6 +147,7 @@ impl Audio {
input_config,
input_stream,
input_buffer,
+ input_volume_sender,
input_channel_receiver: Some(input_receiver),
client_streams,
}
@@ -195,6 +201,10 @@ impl Audio {
pub fn clear_clients(&mut self) {
self.client_streams.lock().unwrap().clear();
}
+
+ pub fn set_input_volume(&self, input_volume: f32) {
+ self.input_volume_sender.broadcast(input_volume).unwrap();
+ }
}
impl ClientStream {
@@ -280,6 +290,7 @@ fn input_callback<T: Sample>(
mut opus_encoder: opus::Encoder,
mut input_sender: Sender<VoicePacketPayload>,
sample_rate: u32,
+ input_volume_receiver: watch::Receiver<f32>,
opus_frame_size_blocks: u32, // blocks of 2.5ms
) -> impl FnMut(&[T], &InputCallbackInfo) + Send + 'static {
if !(opus_frame_size_blocks == 1
@@ -297,7 +308,10 @@ fn input_callback<T: Sample>(
let buf = Arc::new(Mutex::new(VecDeque::new()));
move |data: &[T], _info: &InputCallbackInfo| {
let mut buf = buf.lock().unwrap();
- let out: Vec<f32> = data.iter().map(|e| e.to_f32()).collect();
+ let input_volume = *input_volume_receiver.borrow();
+ let out: Vec<f32> = data.iter().map(|e| e.to_f32())
+ .map(|e| e * input_volume)
+ .collect();
buf.extend(out);
while buf.len() >= opus_frame_size as usize {
let tail = buf.split_off(opus_frame_size as usize);
@@ -308,7 +322,6 @@ fn input_callback<T: Sample>(
opus_buf.truncate(result);
let bytes = Bytes::copy_from_slice(&opus_buf);
match input_sender.try_send(VoicePacketPayload::Opus(bytes, false)) {
- //TODO handle full buffer / disconnect
Ok(_) => {}
Err(_e) => {
//warn!("Error sending audio packet: {:?}", e);
diff --git a/mumd/src/network/tcp.rs b/mumd/src/network/tcp.rs
index ea4ef86..88d2b59 100644
--- a/mumd/src/network/tcp.rs
+++ b/mumd/src/network/tcp.rs
@@ -233,7 +233,6 @@ async fn listen(
break;
}
Some(Some(packet)) => {
- //TODO handle types separately
match packet.unwrap() {
ControlPacket::TextMessage(msg) => {
info!(
diff --git a/mumd/src/state.rs b/mumd/src/state.rs
index 1c964b3..55fd8ae 100644
--- a/mumd/src/state.rs
+++ b/mumd/src/state.rs
@@ -6,7 +6,7 @@ use mumble_protocol::control::msgs;
use mumble_protocol::control::ControlPacket;
use mumble_protocol::voice::Serverbound;
use mumlib::command::{Command, CommandResponse};
-use mumlib::error::Error;
+use mumlib::error::{ChannelIdentifierError, Error};
use serde::{Deserialize, Serialize};
use std::collections::hash_map::Entry;
use std::collections::HashMap;
@@ -50,18 +50,38 @@ impl State {
command: Command,
) -> (bool, mumlib::error::Result<Option<CommandResponse>>) {
match command {
- Command::ChannelJoin { channel_id } => {
+ Command::ChannelJoin { channel_identifier } => {
if !matches!(*self.phase_receiver().borrow(), StatePhase::Connected) {
return (false, Err(Error::DisconnectedError));
}
- let server = self.server.as_ref().unwrap();
- if !server.channels().contains_key(&channel_id) {
- return (false, Err(Error::InvalidChannelIdError(channel_id)));
- }
+
+ let channels = self.server()
+ .unwrap()
+ .channels();
+
+ let matches = channels.iter()
+ .map(|e| (e.0, e.1.path(channels)))
+ .filter(|e| e.1.ends_with(&channel_identifier))
+ .collect::<Vec<_>>();
+ let id = match matches.len() {
+ 0 => {
+ let soft_matches = channels.iter()
+ .map(|e| (e.0, e.1.path(channels).to_lowercase()))
+ .filter(|e| e.1.ends_with(&channel_identifier.to_lowercase()))
+ .collect::<Vec<_>>();
+ match soft_matches.len() {
+ 0 => return (false, Err(Error::ChannelIdentifierError(channel_identifier, ChannelIdentifierError::Invalid))),
+ 1 => *soft_matches.get(0).unwrap().0,
+ _ => return (false, Err(Error::ChannelIdentifierError(channel_identifier, ChannelIdentifierError::Invalid))),
+ }
+ },
+ 1 => *matches.get(0).unwrap().0,
+ _ => return (false, Err(Error::ChannelIdentifierError(channel_identifier, ChannelIdentifierError::Ambiguous))),
+ };
let mut msg = msgs::UserState::new();
- msg.set_session(server.session_id.unwrap());
- msg.set_channel_id(channel_id);
+ msg.set_session(self.server.as_ref().unwrap().session_id.unwrap());
+ msg.set_channel_id(id);
self.packet_sender.send(msg.into()).unwrap();
(false, Ok(None))
}
@@ -141,6 +161,10 @@ impl State {
.unwrap();
(false, Ok(None))
}
+ Command::InputVolumeSet(volume) => {
+ self.audio.set_input_volume(volume);
+ (false, Ok(None))
+ }
}
}
@@ -192,6 +216,9 @@ impl State {
pub fn phase_receiver(&self) -> watch::Receiver<StatePhase> {
self.phase_watcher.1.clone()
}
+ pub fn server(&self) -> Option<&Server> {
+ self.server.as_ref()
+ }
pub fn server_mut(&mut self) -> Option<&mut Server> {
self.server.as_mut()
}
@@ -347,6 +374,13 @@ impl Channel {
pub fn name(&self) -> &str {
&self.name
}
+
+ pub fn path(&self, channels: &HashMap<u32, Channel>) -> String {
+ match &self.parent {
+ Some(t) => format!("{}/{}", channels.get(t).unwrap().path(channels), self.name),
+ None => self.name.clone(),
+ }
+ }
}
#[derive(Debug)]
diff --git a/mumlib/src/command.rs b/mumlib/src/command.rs
index e8f4a4d..b4ab07a 100644
--- a/mumlib/src/command.rs
+++ b/mumlib/src/command.rs
@@ -5,9 +5,10 @@ use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Command {
ChannelJoin {
- channel_id: u32,
+ channel_identifier: String,
},
ChannelList,
+ InputVolumeSet(f32),
ServerConnect {
host: String,
port: u16,
diff --git a/mumlib/src/error.rs b/mumlib/src/error.rs
index 9728f32..a4c6dcb 100644
--- a/mumlib/src/error.rs
+++ b/mumlib/src/error.rs
@@ -8,19 +8,33 @@ pub type Result<T> = std::result::Result<T, Error>;
pub enum Error {
DisconnectedError,
AlreadyConnectedError,
- InvalidChannelIdError(u32),
+ ChannelIdentifierError(String, ChannelIdentifierError),
InvalidServerAddrError(String, u16),
}
+
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Error::DisconnectedError => write!(f, "Not connected to a server"),
Error::AlreadyConnectedError => write!(f, "Already connected to a server"),
- Error::InvalidChannelIdError(id) => write!(f, "Invalid channel id: {}", id),
- Error::InvalidServerAddrError(addr, port) => {
- write!(f, "Invalid server address: {}:{}", addr, port)
- }
+ Error::ChannelIdentifierError(id, kind) => write!(f, "{}: {}", kind, id),
+ Error::InvalidServerAddrError(addr, port) => write!(f, "Invalid server address: {}: {}", addr, port),
+ }
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub enum ChannelIdentifierError {
+ Invalid,
+ Ambiguous,
+}
+
+impl Display for ChannelIdentifierError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ ChannelIdentifierError::Invalid => write!(f, "Invalid channel identifier"),
+ ChannelIdentifierError::Ambiguous => write!(f, "Ambiguous channel identifier"),
}
}
}
diff --git a/usage.org b/usage.org
index e340165..8746014 100644
--- a/usage.org
+++ b/usage.org
@@ -50,7 +50,7 @@ root [3](4)
someone eating food
#+END_SRC
-**** TODO --short
+**** DONE --short
#+BEGIN_SRC bash
$ mumctl channel list --short
root [3](4)
@@ -139,3 +139,9 @@ server offered invalid key. what do you want to do?
#+BEGIN_SRC bash
$ mumctl server rename loopback my_server
#+END_SRC
+** config
+#+BEGIN_SRC bash
+$ mumctl config audio.input_volume 1.1
+$ mumctl config audio.input_volume
+$ mumctl config audio.input_volume --help
+#+END_SRC