Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- [connect] Add command line parameter for setting volume steps.
- [connect] Add support for `seek_to`, `repeat_track` and `autoplay` for `Spirc` loading
- [connect] Add `pause` parameter to `Spirc::disconnect` method (breaking)
- [connect] Add `volume_steps` to `ConnectConfig` (breaking)
Expand Down
12 changes: 7 additions & 5 deletions connect/src/spirc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ enum SpircCommand {
const CONTEXT_FETCH_THRESHOLD: usize = 2;

// delay to update volume after a certain amount of time, instead on each update request
const VOLUME_UPDATE_DELAY: Duration = Duration::from_secs(2);
const VOLUME_UPDATE_DELAY: Duration = Duration::from_millis(500);
Comment thread
photovoltex marked this conversation as resolved.
// to reduce updates to remote, we group some request by waiting for a set amount of time
const UPDATE_STATE_DELAY: Duration = Duration::from_millis(200);

Expand Down Expand Up @@ -1514,16 +1514,16 @@ impl SpircTask {
}

fn handle_volume_up(&mut self) {
let volume_steps = self.connect_state.device_info().capabilities.volume_steps as u16;
let volume = (self.connect_state.device_info().volume as u16)
.saturating_add(self.connect_state.volume_step_size);

let volume = (self.connect_state.device_info().volume as u16).saturating_add(volume_steps);
self.set_volume(volume);
}

fn handle_volume_down(&mut self) {
let volume_steps = self.connect_state.device_info().capabilities.volume_steps as u16;
let volume = (self.connect_state.device_info().volume as u16)
.saturating_sub(self.connect_state.volume_step_size);

let volume = (self.connect_state.device_info().volume as u16).saturating_sub(volume_steps);
self.set_volume(volume);
}

Expand Down Expand Up @@ -1639,6 +1639,8 @@ impl SpircTask {
}

fn set_volume(&mut self, volume: u16) {
debug!("SpircTask::set_volume({})", volume);

let old_volume = self.connect_state.device_info().volume;
let new_volume = volume as u32;
if old_volume != new_volume || self.mixer.volume() != volume {
Expand Down
10 changes: 8 additions & 2 deletions connect/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub struct ConnectConfig {
pub initial_volume: u16,
/// Disables the option to control the volume remotely (default: false)
pub disable_volume: bool,
/// The steps in which the volume is incremented (default: 1024)
/// Number of incremental steps (default: 64)
pub volume_steps: u16,
}

Expand All @@ -99,7 +99,7 @@ impl Default for ConnectConfig {
is_group: false,
initial_volume: u16::MAX / 2,
disable_volume: false,
volume_steps: 1024,
volume_steps: 64,
}
}
}
Expand Down Expand Up @@ -127,10 +127,15 @@ pub(super) struct ConnectState {

/// a context to keep track of the autoplay context
autoplay_context: Option<StateContext>,

/// The volume adjustment per step when handling individual volume adjustments.
pub volume_step_size: u16,
}

impl ConnectState {
pub fn new(cfg: ConnectConfig, session: &Session) -> Self {
let volume_step_size = u16::MAX.checked_div(cfg.volume_steps).unwrap_or(1024);

let device_info = DeviceInfo {
can_play: true,
volume: cfg.initial_volume.into(),
Expand Down Expand Up @@ -195,6 +200,7 @@ impl ConnectState {
}),
..Default::default()
},
volume_step_size,
..Default::default()
};
state.reset();
Expand Down
54 changes: 38 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ fn get_setup() -> Setup {
const VERSION: &str = "version";
const VOLUME_CTRL: &str = "volume-ctrl";
const VOLUME_RANGE: &str = "volume-range";
const VOLUME_STEPS: &str = "volume-steps";
const ZEROCONF_PORT: &str = "zeroconf-port";
const ZEROCONF_INTERFACE: &str = "zeroconf-interface";
const ZEROCONF_BACKEND: &str = "zeroconf-backend";
Expand All @@ -291,6 +292,7 @@ fn get_setup() -> Setup {
const DEVICE_SHORT: &str = "d";
const VOLUME_CTRL_SHORT: &str = "E";
const VOLUME_RANGE_SHORT: &str = "e";
const VOLUME_STEPS_SHORT: &str = ""; // no short flag
const DEVICE_TYPE_SHORT: &str = "F";
const FORMAT_SHORT: &str = "f";
const DISABLE_AUDIO_CACHE_SHORT: &str = "G";
Expand Down Expand Up @@ -371,6 +373,8 @@ fn get_setup() -> Setup {
#[cfg(not(feature = "alsa-backend"))]
const VOLUME_RANGE_DESC: &str =
"Range of the volume control (dB) from 0.0 to 100.0. Defaults to 60.0.";
const VOLUME_STEPS_DESC: &str =
"Number of incremental steps when responding to volume control updates. Defaults to 64.";

let mut opts = getopts::Options::new();
opts.optflag(
Expand Down Expand Up @@ -570,6 +574,12 @@ fn get_setup() -> Setup {
VOLUME_RANGE_DESC,
"RANGE",
)
.optopt(
VOLUME_STEPS_SHORT,
VOLUME_STEPS,
VOLUME_STEPS_DESC,
"STEPS",
)
.optopt(
NORMALISATION_METHOD_SHORT,
NORMALISATION_METHOD,
Expand Down Expand Up @@ -1457,7 +1467,8 @@ fn get_setup() -> Setup {
} else {
cache.as_ref().and_then(Cache::volume)
}
});
})
.unwrap_or_default();

let device_type = opt_str(DEVICE_TYPE)
.as_deref()
Expand All @@ -1480,23 +1491,34 @@ fn get_setup() -> Setup {
})
.unwrap_or_default();

let volume_steps = opt_str(VOLUME_STEPS)
.map(|steps| match steps.parse::<u16>() {
Ok(value) => value,
_ => {
let default_value = &connect_default_config.volume_steps.to_string();

invalid_error_msg(
VOLUME_STEPS,
VOLUME_STEPS_SHORT,
&steps,
"a positive whole number <= 65535",
Comment thread
photovoltex marked this conversation as resolved.
default_value,
);

exit(1);
}
})
.unwrap_or_else(|| connect_default_config.volume_steps);

let is_group = opt_present(DEVICE_IS_GROUP);

if let Some(initial_volume) = initial_volume {
ConnectConfig {
name,
device_type,
is_group,
initial_volume,
..Default::default()
}
} else {
ConnectConfig {
name,
device_type,
is_group,
..Default::default()
}
ConnectConfig {
name,
device_type,
is_group,
initial_volume,
volume_steps,
Comment thread
photovoltex marked this conversation as resolved.
..connect_default_config
}
};

Expand Down