Skip to content

Commit 4370aa1

Browse files
authored
Merge pull request #910 from JasonLG1979/fix-alsa-mixer-device
Fix auto fallback for --alsa-mixer-device and --alsa-mixer-index
2 parents e5fd7d6 + 305f80b commit 4370aa1

2 files changed

Lines changed: 131 additions & 31 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2424
### Fixed
2525
- [main] Prevent hang when discovery is disabled and there are no credentials or when bad credentials are given.
2626
- [main] Don't panic when parsing options. Instead list valid values and exit.
27+
- [main] `--alsa-mixer-device` and `--alsa-mixer-index` now fallback to the card and index specified in `--device`.
2728

2829
### Removed
2930
- [playback] `alsamixer`: previously deprecated option `mixer-card` has been removed.

src/main.rs

Lines changed: 130 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -805,40 +805,135 @@ fn get_setup() -> Setup {
805805
exit(1);
806806
});
807807

808+
let is_alsa_mixer = match mixer_type.as_deref() {
809+
#[cfg(feature = "alsa-backend")]
810+
Some(AlsaMixer::NAME) => true,
811+
_ => false,
812+
};
813+
814+
#[cfg(feature = "alsa-backend")]
815+
if !is_alsa_mixer {
816+
for a in &[ALSA_MIXER_DEVICE, ALSA_MIXER_INDEX, ALSA_MIXER_CONTROL] {
817+
if opt_present(a) {
818+
warn!("Alsa specific mixer options have no effect if not using the alsa mixer.");
819+
break;
820+
}
821+
}
822+
}
823+
808824
let mixer_config = {
809825
let mixer_default_config = MixerConfig::default();
810826

811827
#[cfg(feature = "alsa-backend")]
812-
let device = opt_str(ALSA_MIXER_DEVICE).unwrap_or_else(|| {
813-
if let Some(ref device_name) = device {
814-
device_name.to_string()
815-
} else {
816-
mixer_default_config.device.clone()
817-
}
818-
});
828+
let index = if !is_alsa_mixer {
829+
mixer_default_config.index
830+
} else {
831+
opt_str(ALSA_MIXER_INDEX)
832+
.map(|index| {
833+
index.parse::<u32>().unwrap_or_else(|_| {
834+
invalid_error_msg(
835+
ALSA_MIXER_INDEX,
836+
ALSA_MIXER_INDEX_SHORT,
837+
&index,
838+
"",
839+
&mixer_default_config.index.to_string(),
840+
);
841+
842+
exit(1);
843+
})
844+
})
845+
.unwrap_or_else(|| match device {
846+
// Look for the dev index portion of --device.
847+
// Specifically <dev index> when --device is <something>:CARD=<card name>,DEV=<dev index>
848+
// or <something>:<card index>,<dev index>.
849+
850+
// If --device does not contain a ',' it does not contain a dev index.
851+
// In the case that the dev index is omitted it is assumed to be 0 (mixer_default_config.index).
852+
// Malformed --device values will also fallback to mixer_default_config.index.
853+
Some(ref device_name) if device_name.contains(',') => {
854+
// Turn <something>:CARD=<card name>,DEV=<dev index> or <something>:<card index>,<dev index>
855+
// into DEV=<dev index> or <dev index>.
856+
let dev = &device_name[device_name.find(',').unwrap_or_default()..]
857+
.trim_start_matches(',');
858+
859+
// Turn DEV=<dev index> into <dev index> (noop if it's already <dev index>)
860+
// and then parse <dev index>.
861+
// Malformed --device values will fail the parse and fallback to mixer_default_config.index.
862+
dev[dev.find('=').unwrap_or_default()..]
863+
.trim_start_matches('=')
864+
.parse::<u32>()
865+
.unwrap_or(mixer_default_config.index)
866+
}
867+
_ => mixer_default_config.index,
868+
})
869+
};
819870

820871
#[cfg(not(feature = "alsa-backend"))]
821-
let device = mixer_default_config.device;
872+
let index = mixer_default_config.index;
822873

823874
#[cfg(feature = "alsa-backend")]
824-
let index = opt_str(ALSA_MIXER_INDEX)
825-
.map(|index| {
826-
index.parse::<u32>().unwrap_or_else(|_| {
827-
invalid_error_msg(
828-
ALSA_MIXER_INDEX,
829-
ALSA_MIXER_INDEX_SHORT,
830-
&index,
831-
"",
832-
&mixer_default_config.index.to_string(),
833-
);
875+
let device = if !is_alsa_mixer {
876+
mixer_default_config.device
877+
} else {
878+
match opt_str(ALSA_MIXER_DEVICE) {
879+
Some(mixer_device) => {
880+
if mixer_device.is_empty() {
881+
empty_string_error_msg(ALSA_MIXER_DEVICE, ALSA_MIXER_DEVICE_SHORT);
882+
}
834883

835-
exit(1);
836-
})
837-
})
838-
.unwrap_or_else(|| mixer_default_config.index);
884+
mixer_device
885+
}
886+
None => match device {
887+
Some(ref device_name) => {
888+
// Look for the card name or card index portion of --device.
889+
// Specifically <card name> when --device is <something>:CARD=<card name>,DEV=<dev index>
890+
// or card index when --device is <something>:<card index>,<dev index>.
891+
// --device values like `pulse`, `default`, `jack` may be valid but there is no way to
892+
// infer automatically what the mixer should be so they fail auto fallback
893+
// so --alsa-mixer-device must be manually specified in those situations.
894+
let start_index = device_name.find(':').unwrap_or_default();
895+
896+
let end_index = match device_name.find(',') {
897+
Some(index) if index > start_index => index,
898+
_ => device_name.len(),
899+
};
900+
901+
let card = &device_name[start_index..end_index];
902+
903+
if card.starts_with(':') {
904+
// mixers are assumed to be hw:CARD=<card name> or hw:<card index>.
905+
"hw".to_owned() + card
906+
} else {
907+
error!(
908+
"Could not find an alsa mixer for \"{}\", it must be specified with `--{}` / `-{}`",
909+
&device.unwrap_or_default(),
910+
ALSA_MIXER_DEVICE,
911+
ALSA_MIXER_DEVICE_SHORT
912+
);
913+
914+
exit(1);
915+
}
916+
}
917+
None => {
918+
error!(
919+
"`--{}` / `-{}` or `--{}` / `-{}` \
920+
must be specified when `--{}` / `-{}` is set to \"alsa\"",
921+
DEVICE,
922+
DEVICE_SHORT,
923+
ALSA_MIXER_DEVICE,
924+
ALSA_MIXER_DEVICE_SHORT,
925+
MIXER_TYPE,
926+
MIXER_TYPE_SHORT
927+
);
928+
929+
exit(1);
930+
}
931+
},
932+
}
933+
};
839934

840935
#[cfg(not(feature = "alsa-backend"))]
841-
let index = mixer_default_config.index;
936+
let device = mixer_default_config.device;
842937

843938
#[cfg(feature = "alsa-backend")]
844939
let control = opt_str(ALSA_MIXER_CONTROL).unwrap_or(mixer_default_config.control);
@@ -881,10 +976,12 @@ fn get_setup() -> Setup {
881976
exit(1);
882977
}
883978
})
884-
.unwrap_or_else(|| match mixer_type.as_deref() {
885-
#[cfg(feature = "alsa-backend")]
886-
Some(AlsaMixer::NAME) => 0.0, // let alsa query the control
887-
_ => VolumeCtrl::DEFAULT_DB_RANGE,
979+
.unwrap_or_else(|| {
980+
if is_alsa_mixer {
981+
0.0
982+
} else {
983+
VolumeCtrl::DEFAULT_DB_RANGE
984+
}
888985
});
889986

890987
let volume_ctrl = opt_str(VOLUME_CTRL)
@@ -1093,10 +1190,12 @@ fn get_setup() -> Setup {
10931190

10941191
(volume as f32 / 100.0 * VolumeCtrl::MAX_VOLUME as f32) as u16
10951192
})
1096-
.or_else(|| match mixer_type.as_deref() {
1097-
#[cfg(feature = "alsa-backend")]
1098-
Some(AlsaMixer::NAME) => None,
1099-
_ => cache.as_ref().and_then(Cache::volume),
1193+
.or_else(|| {
1194+
if is_alsa_mixer {
1195+
None
1196+
} else {
1197+
cache.as_ref().and_then(Cache::volume)
1198+
}
11001199
});
11011200

11021201
let device_type = opt_str(DEVICE_TYPE)

0 commit comments

Comments
 (0)