From 3078dbf912ce8cb12d4c7afdb366bb60dfbd0ecd Mon Sep 17 00:00:00 2001 From: Tuan Nguyen <38831897+justmangoou@users.noreply.github.com> Date: Sat, 27 Jun 2026 15:30:30 +0700 Subject: [PATCH 1/2] initial commit --- assets/encoders/headphonesBig.svg | 3 + assets/encoders/headphonesBigMuted.svg | 6 ++ assets/encoders/microphoneBig.svg | 4 + assets/encoders/microphoneBigMuted.svg | 6 ++ assets/manifest.json | 3 + .../voice_settings/audio_device_utils.rs | 1 + src/actions/voice_settings/volume_control.rs | 102 +++++++++++++++++- src/rpc_events.rs | 6 ++ 8 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 assets/encoders/headphonesBig.svg create mode 100644 assets/encoders/headphonesBigMuted.svg create mode 100644 assets/encoders/microphoneBig.svg create mode 100644 assets/encoders/microphoneBigMuted.svg diff --git a/assets/encoders/headphonesBig.svg b/assets/encoders/headphonesBig.svg new file mode 100644 index 0000000..6c804b5 --- /dev/null +++ b/assets/encoders/headphonesBig.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/encoders/headphonesBigMuted.svg b/assets/encoders/headphonesBigMuted.svg new file mode 100644 index 0000000..eb880e7 --- /dev/null +++ b/assets/encoders/headphonesBigMuted.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/encoders/microphoneBig.svg b/assets/encoders/microphoneBig.svg new file mode 100644 index 0000000..4aff504 --- /dev/null +++ b/assets/encoders/microphoneBig.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/encoders/microphoneBigMuted.svg b/assets/encoders/microphoneBigMuted.svg new file mode 100644 index 0000000..7d3e5d6 --- /dev/null +++ b/assets/encoders/microphoneBigMuted.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/manifest.json b/assets/manifest.json index 27f19aa..9a3916c 100644 --- a/assets/manifest.json +++ b/assets/manifest.json @@ -94,6 +94,9 @@ "Icon": "actions/volumeControl", "Tooltip": "Control the volume levels of audio input and output devices", "Controllers": ["Keypad", "Encoder"], + "Encoder": { + "layout": "$B1" + }, "PropertyInspectorPath": "pi/volumecontrol.html", "DisableAutomaticStates": true, "States": [{ "Image": "actions/volumeControl" }] diff --git a/src/actions/voice_settings/audio_device_utils.rs b/src/actions/voice_settings/audio_device_utils.rs index 3e805fd..a1049ea 100644 --- a/src/actions/voice_settings/audio_device_utils.rs +++ b/src/actions/voice_settings/audio_device_utils.rs @@ -56,6 +56,7 @@ pub struct AudioDeviceWrapper { pub device_id: String, pub volume: f32, pub available_devices: Vec, + pub enable: bool, } impl From for SetVoiceSettingsArgs { diff --git a/src/actions/voice_settings/volume_control.rs b/src/actions/voice_settings/volume_control.rs index 793c9a3..4f0a6ab 100644 --- a/src/actions/voice_settings/volume_control.rs +++ b/src/actions/voice_settings/volume_control.rs @@ -1,6 +1,7 @@ use super::audio_device_utils::{AudioDeviceType, AudioDeviceWrapper, get_audio_device_settings}; use super::update_voice_setting; +use discord_ipc_rust::models::send::commands::SetVoiceSettingsArgs; use openaction::{Action, ActionUuid, Instance, OpenActionResult, async_trait}; use serde::{Deserialize, Serialize}; @@ -32,6 +33,55 @@ impl Default for VolumeControlSettings { } } +const MUTED_ICON: &str = include_str!("../../../assets/encoders/microphoneBigMuted.svg"); +const UNMUTED_ICON: &str = include_str!("../../../assets/encoders/microphoneBig.svg"); +const DEAFEN_ICON: &str = include_str!("../../../assets/encoders/headphonesBigMuted.svg"); +const UNDEAFEN_ICON: &str = include_str!("../../../assets/encoders/headphonesBig.svg"); + +async fn update_feedback( + instance: &Instance, + settings: &VolumeControlSettings, +) -> OpenActionResult<()> { + let device_type = &settings.device_type; + let Some(device_settings) = get_audio_device_settings(device_type).await else { + log::error!( + "Failed to obtain voice settings for {:?} device", + device_type + ); + instance.show_alert().await?; + return Ok(()); + }; + + let icon = match (device_type, device_settings.enable) { + (AudioDeviceType::Input, true) => UNMUTED_ICON, + (AudioDeviceType::Input, false) => MUTED_ICON, + (AudioDeviceType::Output, true) => UNDEAFEN_ICON, + (AudioDeviceType::Output, false) => DEAFEN_ICON, + }; + + let value = device_type.to_linear(device_settings.volume) as i32; + let indicator_value = match device_type { + AudioDeviceType::Input => value, + AudioDeviceType::Output => value / 2, + }; + + let title = match device_type { + AudioDeviceType::Input => "Microphone", + AudioDeviceType::Output => "Headphones", + }; + + instance + .set_feedback(&serde_json::json!({ + "title": title, + "icon": icon, + "value": format!("{}%", value), + "indicator": { + "value": indicator_value + } + })) + .await +} + async fn adjust_volume( instance: &Instance, device_type: &AudioDeviceType, @@ -69,6 +119,14 @@ impl Action for VolumeControlAction { const UUID: ActionUuid = "me.amankhanna.oadiscord.volumecontrol"; type Settings = VolumeControlSettings; + async fn did_receive_settings( + &self, + instance: &Instance, + settings: &Self::Settings, + ) -> OpenActionResult<()> { + update_feedback(instance, settings).await + } + async fn key_up(&self, instance: &Instance, settings: &Self::Settings) -> OpenActionResult<()> { let value = match settings.action_type { VolumeControlActionType::Increase => settings.step_size as f32, @@ -85,6 +143,49 @@ impl Action for VolumeControlAction { .await } + async fn dial_up( + &self, + instance: &Instance, + settings: &Self::Settings, + ) -> OpenActionResult<()> { + let device_type = &settings.device_type; + let Some(device_settings) = get_audio_device_settings(device_type).await else { + log::error!( + "Failed to obtain voice settings for {:?} device", + device_type + ); + instance.show_alert().await?; + return Ok(()); + }; + + match device_type { + AudioDeviceType::Input => { + let new_mute = !device_settings.enable; + update_voice_setting( + instance, + SetVoiceSettingsArgs { + mute: Some(new_mute), + ..Default::default() + }, + if new_mute { 1 } else { 0 }, + ) + .await + } + AudioDeviceType::Output => { + let new_deaf = !device_settings.enable; + update_voice_setting( + instance, + SetVoiceSettingsArgs { + deaf: Some(new_deaf), + ..Default::default() + }, + if new_deaf { 1 } else { 0 }, + ) + .await + } + } + } + async fn dial_rotate( &self, instance: &Instance, @@ -93,7 +194,6 @@ impl Action for VolumeControlAction { _pressed: bool, ) -> OpenActionResult<()> { let delta = (settings.step_size as f32) * ticks as f32; - adjust_volume(instance, &settings.device_type, delta, false).await } } diff --git a/src/rpc_events.rs b/src/rpc_events.rs index b0efed5..abc828e 100644 --- a/src/rpc_events.rs +++ b/src/rpc_events.rs @@ -160,6 +160,7 @@ async fn apply_voice_state(settings: discord_ipc_rust::models::shared::voice::Vo device_id: input.device_id, volume: input.volume, available_devices: input.available_devices, + enable: mute, }); } @@ -171,9 +172,14 @@ async fn apply_voice_state(settings: discord_ipc_rust::models::shared::voice::Vo device_id: output.device_id, volume: output.volume, available_devices: output.available_devices, + enable: deaf, }); } + for instance in visible_instances(crate::actions::VolumeControlAction::UUID).await { + let _ = instance.get_settings().await; + } + for instance in visible_instances(crate::actions::SetAudioDeviceAction::UUID).await { let _ = crate::actions::voice_settings::set_audio_device::send_available_devices_to_pi( &instance, From 6f73a2ac867369e064c6d8e781edbfe00a117dae Mon Sep 17 00:00:00 2001 From: Tuan Nguyen <38831897+justmangoou@users.noreply.github.com> Date: Sat, 27 Jun 2026 16:26:03 +0700 Subject: [PATCH 2/2] fix: logic --- src/actions/voice_settings/volume_control.rs | 16 ++++++++++++---- src/rpc_events.rs | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/actions/voice_settings/volume_control.rs b/src/actions/voice_settings/volume_control.rs index 4f0a6ab..f0ca215 100644 --- a/src/actions/voice_settings/volume_control.rs +++ b/src/actions/voice_settings/volume_control.rs @@ -119,6 +119,14 @@ impl Action for VolumeControlAction { const UUID: ActionUuid = "me.amankhanna.oadiscord.volumecontrol"; type Settings = VolumeControlSettings; + async fn will_appear( + &self, + instance: &Instance, + settings: &Self::Settings, + ) -> OpenActionResult<()> { + update_feedback(instance, settings).await + } + async fn did_receive_settings( &self, instance: &Instance, @@ -160,26 +168,26 @@ impl Action for VolumeControlAction { match device_type { AudioDeviceType::Input => { - let new_mute = !device_settings.enable; + let new_mute = device_settings.enable; update_voice_setting( instance, SetVoiceSettingsArgs { mute: Some(new_mute), ..Default::default() }, - if new_mute { 1 } else { 0 }, + 0, ) .await } AudioDeviceType::Output => { - let new_deaf = !device_settings.enable; + let new_deaf = device_settings.enable; update_voice_setting( instance, SetVoiceSettingsArgs { deaf: Some(new_deaf), ..Default::default() }, - if new_deaf { 1 } else { 0 }, + 0, ) .await } diff --git a/src/rpc_events.rs b/src/rpc_events.rs index abc828e..cd1e6da 100644 --- a/src/rpc_events.rs +++ b/src/rpc_events.rs @@ -160,7 +160,7 @@ async fn apply_voice_state(settings: discord_ipc_rust::models::shared::voice::Vo device_id: input.device_id, volume: input.volume, available_devices: input.available_devices, - enable: mute, + enable: !mute && !deaf, }); } @@ -172,7 +172,7 @@ async fn apply_voice_state(settings: discord_ipc_rust::models::shared::voice::Vo device_id: output.device_id, volume: output.volume, available_devices: output.available_devices, - enable: deaf, + enable: !deaf, }); }