From 0e5054a4c5f8a994b2c71467203c770b84416dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vuka=C5=A1in=20Vojinovi=C4=87?= <150025636+git-f0x@users.noreply.github.com> Date: Fri, 12 Sep 2025 13:37:52 +0200 Subject: [PATCH] feat(sound): volume amplification This adds toggles that allow increasing volume to 150% for both the sink and source. --- cosmic-settings/src/pages/sound.rs | 107 +++++++++++++++++++++++++---- i18n/en/cosmic_settings.ftl | 3 + i18n/sr-Cyrl/cosmic_settings.ftl | 3 + i18n/sr-Latn/cosmic_settings.ftl | 3 + 4 files changed, 104 insertions(+), 12 deletions(-) diff --git a/cosmic-settings/src/pages/sound.rs b/cosmic-settings/src/pages/sound.rs index e5a1a54..9eaa6a6 100644 --- a/cosmic-settings/src/pages/sound.rs +++ b/cosmic-settings/src/pages/sound.rs @@ -7,12 +7,17 @@ use cosmic::{ surface, widget::{self, settings}, }; +use cosmic_config::{Config, ConfigGet, ConfigSet}; use cosmic_settings_page::{self as page, Section, section}; use slab::Slab; use slotmap::SlotMap; use cosmic_settings_subscriptions::sound as subscription; +const AUDIO_CONFIG: &str = "com.system76.CosmicAudio"; +const AMPLIFICATION_SINK: &str = "amplification_sink"; +const AMPLIFICATION_SOURCE: &str = "amplification_source"; + #[derive(Clone, Debug)] pub enum Message { /// Change the balance of the active sink. @@ -25,6 +30,8 @@ pub enum Message { SinkProfileChanged(usize), /// Request to change the default output volume. SinkVolumeChanged(u32), + /// Toggle amplification for sink + ToggleOverAmplificationSink(bool), /// Change the default input output. SourceChanged(usize), /// Toggle the mute status of the input output. @@ -33,6 +40,8 @@ pub enum Message { SourceProfileChanged(usize), /// Request to change the input volume. SourceVolumeChanged(u32), + /// Toggle amplification for sink + ToggleOverAmplificationSource(bool), /// Messages handled by the sound module in cosmic-settings-subscriptions Subscription(subscription::Message), /// Surface Action @@ -61,9 +70,29 @@ impl Into for subscription::Message { pub struct Page { entity: page::Entity, model: subscription::Model, + sound_config: Option, + amplification_sink: bool, + amplification_source: bool, } impl page::Page for Page { + fn on_enter(&mut self) -> cosmic::Task { + match Config::new(AUDIO_CONFIG, 1) { + Ok(config) => { + self.amplification_sink = config.get::(AMPLIFICATION_SINK).unwrap_or(true); + self.amplification_source = + config.get::(AMPLIFICATION_SOURCE).unwrap_or(false); + self.sound_config = Some(config); + } + Err(why) => { + tracing::error!(?why, "Failed to load sound config"); + self.amplification_sink = true; + self.amplification_source = false; + } + } + Task::none() + } + fn content( &self, sections: &mut SlotMap>, @@ -131,6 +160,16 @@ impl Page { .map(|message| Message::Subscription(message).into()); } + Message::ToggleOverAmplificationSink(enabled) => { + self.amplification_sink = enabled; + + if let Some(config) = &self.sound_config { + if let Err(why) = config.set(AMPLIFICATION_SINK, enabled) { + tracing::error!(?why, "Failed to save over amplification setting"); + } + } + } + Message::SourceChanged(pos) => { return self .model @@ -154,6 +193,16 @@ impl Page { .map(|message| Message::Subscription(message).into()); } + Message::ToggleOverAmplificationSource(enabled) => { + self.amplification_source = enabled; + + if let Some(config) = &self.sound_config { + if let Err(why) = config.set(AMPLIFICATION_SOURCE, enabled) { + tracing::error!(?why, "Failed to save over amplification setting"); + } + } + } + Message::Subscription(message) => { return self .model @@ -175,6 +224,8 @@ fn input() -> Section { let device = descriptions.insert(fl!("sound-input", "device")); let _level = descriptions.insert(fl!("sound-input", "level")); let profile = descriptions.insert(fl!("profile")); + let amplification = descriptions.insert(fl!("amplification")); + let amplification_desc = descriptions.insert(fl!("amplification", "desc")); Section::default() .title(fl!("sound-input")) @@ -184,6 +235,17 @@ fn input() -> Section { return widget::row().into(); } + let slider = if page.amplification_source { + widget::slider(0..=150, page.model.source_volume, |change| { + Message::SourceVolumeChanged(change).into() + }) + .breakpoints(&[100]) + } else { + widget::slider(0..=100, page.model.source_volume, |change| { + Message::SourceVolumeChanged(change).into() + }) + }; + let volume_control = widget::row::with_capacity(4) .align_y(Alignment::Center) .push( @@ -200,12 +262,7 @@ fn input() -> Section { .align_x(Alignment::Center), ) .push(widget::horizontal_space().width(8)) - .push( - widget::slider(0..=150, page.model.source_volume, |change| { - Message::SourceVolumeChanged(change).into() - }) - .breakpoints(&[100]), - ); + .push(slider); let devices = widget::dropdown::popup_dropdown( page.model.sources(), Some(page.model.active_source().unwrap_or(0)), @@ -240,6 +297,15 @@ fn input() -> Section { controls = controls.add(settings::item(&*section.descriptions[profile], dropdown)); } + controls = controls.add( + settings::item::builder(&*section.descriptions[amplification]) + .description(&*section.descriptions[amplification_desc]) + .control( + widget::toggler(page.amplification_source) + .on_toggle(|t| Message::ToggleOverAmplificationSource(t).into()), + ), + ); + Element::from(controls) }) } @@ -255,11 +321,24 @@ fn output() -> Section { let left = descriptions.insert(fl!("sound-output", "left")); let right = descriptions.insert(fl!("sound-output", "right")); // let balance = descriptions.insert(fl!("sound-output", "balance")); + let amplification = descriptions.insert(fl!("amplification")); + let amplification_desc = descriptions.insert(fl!("amplification", "desc")); Section::default() .title(fl!("sound-output")) .descriptions(descriptions) .view::(move |_binder, page, section| { + let slider = if page.amplification_sink { + widget::slider(0..=150, page.model.sink_volume, |change| { + Message::SinkVolumeChanged(change).into() + }) + .breakpoints(&[100]) + } else { + widget::slider(0..=100, page.model.sink_volume, |change| { + Message::SinkVolumeChanged(change).into() + }) + }; + let volume_control = widget::row::with_capacity(4) .align_y(Alignment::Center) .push( @@ -276,12 +355,7 @@ fn output() -> Section { .align_x(Alignment::Center), ) .push(widget::horizontal_space().width(8)) - .push( - widget::slider(0..=150, page.model.sink_volume, |change| { - Message::SinkVolumeChanged(change).into() - }) - .breakpoints(&[100]), - ); + .push(slider); let devices = widget::dropdown::popup_dropdown( page.model.sinks(), @@ -344,6 +418,15 @@ fn output() -> Section { )); } + controls = controls.add( + settings::item::builder(&*section.descriptions[amplification]) + .description(&*section.descriptions[amplification_desc]) + .control( + widget::toggler(page.amplification_sink) + .on_toggle(|t| Message::ToggleOverAmplificationSink(t).into()), + ), + ); + Element::from(controls) }) } diff --git a/i18n/en/cosmic_settings.ftl b/i18n/en/cosmic_settings.ftl index 933e7ea..e83294d 100644 --- a/i18n/en/cosmic_settings.ftl +++ b/i18n/en/cosmic_settings.ftl @@ -473,6 +473,9 @@ sound-input = Input .device = Input device .level = Input level +amplification = Amplification + .desc = Allows raising the volume to 150%. + sound-alerts = Alerts .volume = Alerts volume .sound = Alerts sound diff --git a/i18n/sr-Cyrl/cosmic_settings.ftl b/i18n/sr-Cyrl/cosmic_settings.ftl index ce23a7d..e6f4df1 100644 --- a/i18n/sr-Cyrl/cosmic_settings.ftl +++ b/i18n/sr-Cyrl/cosmic_settings.ftl @@ -474,6 +474,9 @@ sound-input = Улаз .device = Улазни уређај .level = Ниво улаза +amplification = Појачавање + .desc = Омогућава повећање јачине звука до 150%. + sound-alerts = Упозорења .volume = Јачина звука упозорења .sound = Звук упозорења diff --git a/i18n/sr-Latn/cosmic_settings.ftl b/i18n/sr-Latn/cosmic_settings.ftl index c927bc1..cc3612f 100644 --- a/i18n/sr-Latn/cosmic_settings.ftl +++ b/i18n/sr-Latn/cosmic_settings.ftl @@ -474,6 +474,9 @@ sound-input = Ulaz .device = Ulazni uređaj .level = Nivo ulaza +amplification = Pojačavanje + .desc = Omogućava povećanje jačine zvuka do 150%. + sound-alerts = Upozorenja .volume = Jačina zvuka upozorenja .sound = Zvuk upozorenja