Skip to content

Commit ebc8c43

Browse files
committed
feat(sound): add output test button with playback fallbacks
1 parent 4597822 commit ebc8c43

File tree

3 files changed

+86
-1
lines changed

3 files changed

+86
-1
lines changed

cosmic-settings/src/pages/sound/mod.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub enum Message {
3434
SetSinkVolume(u32),
3535
/// Request to change the input volume.
3636
SetSourceVolume(u32),
37+
TestOutput,
3738
/// Messages handled by the sound module in cosmic-settings-subscriptions
3839
Subscription(subscription::Message),
3940
/// Surface Action
@@ -212,6 +213,10 @@ impl Page {
212213
.map(|message| Message::Subscription(message).into());
213214
}
214215

216+
Message::TestOutput => {
217+
self.model.test_output();
218+
}
219+
215220
Message::ToggleOverAmplificationSink(enabled) => {
216221
self.amplification_sink = enabled;
217222

@@ -334,6 +339,7 @@ fn output() -> Section<crate::pages::Message> {
334339
crate::slab!(descriptions {
335340
volume = fl!("sound-output", "volume");
336341
device = fl!("sound-output", "device");
342+
test = fl!("sound-output", "test");
337343
_level = fl!("sound-output", "level");
338344
balance = fl!("sound-output", "balance");
339345
left = fl!("sound-output", "left");
@@ -389,14 +395,26 @@ fn output() -> Section<crate::pages::Message> {
389395
.apply(Element::from)
390396
.map(crate::pages::Message::from);
391397

398+
let test_output = widget::button::standard(&*section.descriptions[test])
399+
.on_press_maybe(page.model.active_sink().map(|_| Message::TestOutput.into()));
400+
401+
let output_device_controls = widget::row::with_capacity(3)
402+
.align_y(Alignment::Center)
403+
.push(devices)
404+
.push(widget::horizontal_space().width(8))
405+
.push(test_output);
406+
392407
let mut controls = settings::section()
393408
.title(&section.title)
394409
.add(
395410
settings::item::builder(&*section.descriptions[volume])
396411
.flex_control(volume_control)
397412
.align_items(Alignment::Center),
398413
)
399-
.add(settings::item(&*section.descriptions[device], devices))
414+
.add(settings::item(
415+
&*section.descriptions[device],
416+
output_device_controls,
417+
))
400418
.add(settings::item(
401419
&*section.descriptions[balance],
402420
widget::row::with_capacity(4)

i18n/en/cosmic_settings.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ sound = Sound
588588
sound-output = Output
589589
.volume = Output volume
590590
.device = Output device
591+
.test = Test
591592
.level = Output level
592593
.config = Configuration
593594
.balance = Balance

subscriptions/sound/src/lib.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,19 @@ impl Model {
138138
&self.sources
139139
}
140140

141+
pub fn test_output(&self) {
142+
if self.active_sink_node.is_none() {
143+
tracing::warn!(target: "sound", "cannot play output test sound without an active sink");
144+
return;
145+
}
146+
147+
tokio::spawn(async {
148+
if !play_output_test_sound().await {
149+
tracing::warn!(target: "sound", "failed to play output test sound using available backends");
150+
}
151+
});
152+
}
153+
141154
pub fn clear(&mut self) {
142155
if let Some(handle) = self.subscription_handle.take() {
143156
_ = handle.cancel_tx.send(());
@@ -956,3 +969,56 @@ pub async fn set_profile(id: u32, index: u32, save: bool) {
956969
.status()
957970
.await;
958971
}
972+
973+
async fn play_output_test_sound() -> bool {
974+
if run_command("canberra-gtk-play", &["-i", "audio-test-signal"]).await {
975+
return true;
976+
}
977+
978+
let test_sound_paths = [
979+
"/usr/share/sounds/freedesktop/stereo/audio-test-signal.oga",
980+
"/usr/share/sounds/freedesktop/stereo/audio-test-signal.ogg",
981+
"/usr/share/sounds/freedesktop/stereo/audio-test-signal.wav",
982+
"/usr/share/sounds/freedesktop/stereo/bell.oga",
983+
"/usr/share/sounds/freedesktop/stereo/bell.ogg",
984+
"/usr/share/sounds/freedesktop/stereo/bell.wav",
985+
"/usr/share/sounds/freedesktop/stereo/audio-test.ogg",
986+
];
987+
988+
for sound_path in test_sound_paths {
989+
if run_command("paplay", &[sound_path]).await {
990+
return true;
991+
}
992+
}
993+
994+
for sound_path in test_sound_paths {
995+
if run_command("pw-play", &[sound_path]).await {
996+
return true;
997+
}
998+
}
999+
1000+
false
1001+
}
1002+
1003+
async fn run_command(command: &str, args: &[&str]) -> bool {
1004+
match tokio::time::timeout(
1005+
Duration::from_secs(5),
1006+
tokio::process::Command::new(command)
1007+
.args(args)
1008+
.stdout(Stdio::null())
1009+
.stderr(Stdio::null())
1010+
.status(),
1011+
)
1012+
.await
1013+
{
1014+
Ok(Ok(status)) => status.success(),
1015+
Ok(Err(why)) => {
1016+
tracing::debug!(target: "sound", ?why, command, "failed to run test sound command");
1017+
false
1018+
}
1019+
Err(why) => {
1020+
tracing::warn!(target: "sound", ?why, command, "test sound command timed out");
1021+
false
1022+
}
1023+
}
1024+
}

0 commit comments

Comments
 (0)