From 4870acd572c8de86b9c71d308363485111c3725a Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 25 Jan 2018 23:37:28 +0100 Subject: [PATCH 1/5] Add --progressive-volume option. Increase volume slowly at low level, faster at higher level --- core/src/config.rs | 1 + src/main.rs | 11 ++++++++--- src/spirc.rs | 49 ++++++++++++++++++++++++++++++++++++---------- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/core/src/config.rs b/core/src/config.rs index 46b22e41..aab65e69 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -122,4 +122,5 @@ pub struct ConnectConfig { pub name: String, pub device_type: DeviceType, pub volume: i32, + pub progressive_volume: bool, } diff --git a/src/main.rs b/src/main.rs index c6208f2b..27d91d21 100644 --- a/src/main.rs +++ b/src/main.rs @@ -101,7 +101,8 @@ fn setup(args: &[String]) -> Setup { .optopt("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND") .optopt("", "device", "Audio device to use. Use '?' to list options", "DEVICE") .optopt("", "mixer", "Mixer to use", "MIXER") - .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME"); + .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME") + .optflag("", "progressive-volume", "Increase volume slowly at low level, faster at high level"); let matches = match opts.parse(&args[1..]) { Ok(m) => m, @@ -159,8 +160,11 @@ fn setup(args: &[String]) -> Setup { // if argument not present use default values (50%) else{ initial_volume = 0x8000 as i32; - } - debug!("Volume \"{}\" !", initial_volume); + } + + let progressive_volume = matches.opt_present("progressive-volume"); + info!("Volume:{}, progressive_volume: {}.", initial_volume, progressive_volume); + let name = matches.opt_str("name").unwrap(); let use_audio_cache = !matches.opt_present("disable-audio-cache"); @@ -209,6 +213,7 @@ fn setup(args: &[String]) -> Setup { name: name, device_type: device_type, volume: initial_volume, + progressive_volume, } }; diff --git a/src/spirc.rs b/src/spirc.rs index 795ba68f..f60d7ac0 100644 --- a/src/spirc.rs +++ b/src/spirc.rs @@ -17,12 +17,14 @@ use protocol::spirc::{PlayStatus, State, MessageType, Frame, DeviceState}; use mixer::Mixer; use player::Player; +use std; use rand; use rand::Rng; pub struct SpircTask { player: Player, mixer: Box, + progressive_volume:bool, sequence: SeqGenerator, @@ -122,6 +124,33 @@ fn initial_device_state(config: ConnectConfig, volume: u16) -> DeviceState { }) } +fn volume_to_mixer(volume: u16, progressive: bool) -> u16 { + if progressive { + // Some by trail determined volume calculation algorithm. Start increasing slowly, + // then after 50% increase in bigger steps. + let d = volume / (std::u16::MAX / 100); + let mut v:u32 = 0; + let mut incr:u32 = 0; + for i in 0..d { + v += incr; + incr +=3; + if i > 50 { + // Increase faster to reach max volume + incr += 42; + } + } + + // Clip the vulume to the max + if v > std::u16::MAX as u32 {v = std::u16::MAX as u32;} + debug!("volume_to_mixer {} {}", volume, v); + return v as u16; + } else { + debug!("volume_to_mixer {}", volume); + return volume; + } +} + + impl Spirc { pub fn new(config: ConnectConfig, session: Session, player: Player, mixer: Box) -> (Spirc, SpircTask) @@ -144,15 +173,15 @@ impl Spirc { })); let (cmd_tx, cmd_rx) = mpsc::unbounded(); - + let progressive_volume = config.progressive_volume; let volume = config.volume as u16; let device = initial_device_state(config, volume); - mixer.set_volume(volume as u16); + mixer.set_volume(volume_to_mixer(volume as u16, progressive_volume)); let mut task = SpircTask { player: player, mixer: mixer, - + progressive_volume, sequence: SeqGenerator::new(1), ident: ident, @@ -439,9 +468,9 @@ impl SpircTask { } MessageType::kMessageTypeVolume => { - let volume = frame.get_volume(); - self.device.set_volume(volume); - self.mixer.set_volume(frame.get_volume() as u16); + self.device.set_volume(frame.get_volume()); + self.mixer.set_volume( + volume_to_mixer(frame.get_volume() as u16, self.progressive_volume)); self.notify(None); } @@ -536,21 +565,21 @@ impl SpircTask { } fn handle_volume_up(&mut self) { - let mut volume: u32 = self.mixer.volume() as u32 + 4096; + let mut volume: u32 = self.device.get_volume() as u32 + 4096; if volume > 0xFFFF { volume = 0xFFFF; } self.device.set_volume(volume); - self.mixer.set_volume(volume as u16); + self.mixer.set_volume(volume_to_mixer(volume as u16, self.progressive_volume)); } fn handle_volume_down(&mut self) { - let mut volume: i32 = self.mixer.volume() as i32 - 4096; + let mut volume: i32 = self.device.get_volume() as i32 - 4096; if volume < 0 { volume = 0; } self.device.set_volume(volume as u32); - self.mixer.set_volume(volume as u16); + self.mixer.set_volume(volume_to_mixer(volume as u16, self.progressive_volume)); } fn handle_end_of_track(&mut self) { From 67adb06859d951755369ecd887e152e48f34fea4 Mon Sep 17 00:00:00 2001 From: Robert Date: Fri, 26 Jan 2018 01:10:52 +0100 Subject: [PATCH 2/5] update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bd8bf64c..f1d648b9 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ target/release/librespot --username USERNAME --cache CACHEDIR --name DEVICENAME | Option | | device | Audio device to use. Use '?' to list options | DEVICE | | Option | | mixer | Mixer to use | MIXER | | Option | | initial-volume | Initial volume in %, once connected [0-100] | VOLUME | +| Flag | | progressive-volume | Increase volume slowly at low level | | Taken from here: https://github.com/ComlOnline/librespot/blob/master/src/main.rs#L88 From f5b70c4cb6fd09e49e3f4851819853fb0c46005e Mon Sep 17 00:00:00 2001 From: Robert Date: Fri, 26 Jan 2018 01:10:52 +0100 Subject: [PATCH 3/5] update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bd8bf64c..f1d648b9 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ target/release/librespot --username USERNAME --cache CACHEDIR --name DEVICENAME | Option | | device | Audio device to use. Use '?' to list options | DEVICE | | Option | | mixer | Mixer to use | MIXER | | Option | | initial-volume | Initial volume in %, once connected [0-100] | VOLUME | +| Flag | | progressive-volume | Increase volume slowly at low level | | Taken from here: https://github.com/ComlOnline/librespot/blob/master/src/main.rs#L88 From de7d341faa3505b1ff4160f4819196ad9be32b88 Mon Sep 17 00:00:00 2001 From: thekr1s Date: Fri, 26 Jan 2018 01:17:31 +0100 Subject: [PATCH 4/5] update README.md --- README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 README.md diff --git a/README.md b/README.md old mode 100644 new mode 100755 From bec6b8c512ff2d2e7c4aa3d7819e02dd8fca73f8 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 29 Jan 2018 23:37:30 +0100 Subject: [PATCH 5/5] Changed volume control after discussion: https://github.com/librespot-org/librespot/pull/10 implement exponential volume control only --- README.md | 1 - core/src/config.rs | 1 - src/main.rs | 11 +++------ src/spirc.rs | 56 +++++++++++++++++++++------------------------- 4 files changed, 28 insertions(+), 41 deletions(-) mode change 100755 => 100644 README.md diff --git a/README.md b/README.md old mode 100755 new mode 100644 index f1d648b9..bd8bf64c --- a/README.md +++ b/README.md @@ -73,7 +73,6 @@ target/release/librespot --username USERNAME --cache CACHEDIR --name DEVICENAME | Option | | device | Audio device to use. Use '?' to list options | DEVICE | | Option | | mixer | Mixer to use | MIXER | | Option | | initial-volume | Initial volume in %, once connected [0-100] | VOLUME | -| Flag | | progressive-volume | Increase volume slowly at low level | | Taken from here: https://github.com/ComlOnline/librespot/blob/master/src/main.rs#L88 diff --git a/core/src/config.rs b/core/src/config.rs index aab65e69..46b22e41 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -122,5 +122,4 @@ pub struct ConnectConfig { pub name: String, pub device_type: DeviceType, pub volume: i32, - pub progressive_volume: bool, } diff --git a/src/main.rs b/src/main.rs index 27d91d21..c6208f2b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -101,8 +101,7 @@ fn setup(args: &[String]) -> Setup { .optopt("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND") .optopt("", "device", "Audio device to use. Use '?' to list options", "DEVICE") .optopt("", "mixer", "Mixer to use", "MIXER") - .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME") - .optflag("", "progressive-volume", "Increase volume slowly at low level, faster at high level"); + .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME"); let matches = match opts.parse(&args[1..]) { Ok(m) => m, @@ -160,11 +159,8 @@ fn setup(args: &[String]) -> Setup { // if argument not present use default values (50%) else{ initial_volume = 0x8000 as i32; - } - - let progressive_volume = matches.opt_present("progressive-volume"); - info!("Volume:{}, progressive_volume: {}.", initial_volume, progressive_volume); - + } + debug!("Volume \"{}\" !", initial_volume); let name = matches.opt_str("name").unwrap(); let use_audio_cache = !matches.opt_present("disable-audio-cache"); @@ -213,7 +209,6 @@ fn setup(args: &[String]) -> Setup { name: name, device_type: device_type, volume: initial_volume, - progressive_volume, } }; diff --git a/src/spirc.rs b/src/spirc.rs index f60d7ac0..422340ae 100644 --- a/src/spirc.rs +++ b/src/spirc.rs @@ -24,7 +24,6 @@ use rand::Rng; pub struct SpircTask { player: Player, mixer: Box, - progressive_volume:bool, sequence: SeqGenerator, @@ -124,30 +123,26 @@ fn initial_device_state(config: ConnectConfig, volume: u16) -> DeviceState { }) } -fn volume_to_mixer(volume: u16, progressive: bool) -> u16 { - if progressive { - // Some by trail determined volume calculation algorithm. Start increasing slowly, - // then after 50% increase in bigger steps. - let d = volume / (std::u16::MAX / 100); - let mut v:u32 = 0; - let mut incr:u32 = 0; - for i in 0..d { - v += incr; - incr +=3; - if i > 50 { - // Increase faster to reach max volume - incr += 42; - } - } - - // Clip the vulume to the max - if v > std::u16::MAX as u32 {v = std::u16::MAX as u32;} - debug!("volume_to_mixer {} {}", volume, v); - return v as u16; - } else { - debug!("volume_to_mixer {}", volume); - return volume; +fn volume_to_mixer(volume: u16) -> u16 { + // Volume conversion taken from https://www.dr-lex.be/info-stuff/volumecontrols.html#ideal2 + // Convert the given volume [0..0xffff] to a dB gain + // We assume a dB range of 60dB. + // Use the equatation: a * exp(b * x) + // in which a = IDEAL_FACTOR, b = 1/1000 + const IDEAL_FACTOR: f64 = 6.908; + let normalized_volume = volume as f64 / std::u16::MAX as f64; // To get a value between 0 and 1 + + let mut val = std::u16::MAX; + // Prevent val > std::u16::MAX due to rounding errors + if normalized_volume < 0.999 { + let new_volume = (normalized_volume * IDEAL_FACTOR).exp() / 1000.0; + val = (new_volume * std::u16::MAX as f64) as u16; } + + debug!("input volume:{} to mixer: {}", volume, val); + + // return the scale factor (0..0xffff) (equivalent to a voltage multiplier). + val } @@ -173,15 +168,15 @@ impl Spirc { })); let (cmd_tx, cmd_rx) = mpsc::unbounded(); - let progressive_volume = config.progressive_volume; + let volume = config.volume as u16; let device = initial_device_state(config, volume); - mixer.set_volume(volume_to_mixer(volume as u16, progressive_volume)); + mixer.set_volume(volume_to_mixer(volume as u16)); let mut task = SpircTask { player: player, mixer: mixer, - progressive_volume, + sequence: SeqGenerator::new(1), ident: ident, @@ -469,8 +464,7 @@ impl SpircTask { MessageType::kMessageTypeVolume => { self.device.set_volume(frame.get_volume()); - self.mixer.set_volume( - volume_to_mixer(frame.get_volume() as u16, self.progressive_volume)); + self.mixer.set_volume(volume_to_mixer(frame.get_volume() as u16)); self.notify(None); } @@ -570,7 +564,7 @@ impl SpircTask { volume = 0xFFFF; } self.device.set_volume(volume); - self.mixer.set_volume(volume_to_mixer(volume as u16, self.progressive_volume)); + self.mixer.set_volume(volume_to_mixer(volume as u16)); } fn handle_volume_down(&mut self) { @@ -579,7 +573,7 @@ impl SpircTask { volume = 0; } self.device.set_volume(volume as u32); - self.mixer.set_volume(volume_to_mixer(volume as u16, self.progressive_volume)); + self.mixer.set_volume(volume_to_mixer(volume as u16)); } fn handle_end_of_track(&mut self) {