1use std::path::{Path, PathBuf};
2use std::thread;
3use std::time::Duration;
4
5use crate::config::SoundConfig;
6
7#[derive(Clone, Copy)]
10pub enum SoundEvent {
11 SyncStart,
12 SyncDone,
13 SyncError,
14}
15
16pub fn play_event_sound(config: &SoundConfig, event: SoundEvent) {
23 let path = match event {
24 SoundEvent::SyncStart => config.sync_start.as_ref(),
25 SoundEvent::SyncDone => config.sync_done.as_ref(),
26 SoundEvent::SyncError => config.sync_error.as_ref(),
27 };
28
29 match path {
30 Some(p) => play_file_async(p.clone()),
31 None => play_default_async(event),
32 }
33}
34
35fn play_file_async(path: PathBuf) {
38 thread::spawn(move || {
39 let _ = play_file_blocking(&path);
40 });
41}
42
43fn play_file_blocking(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
44 let handle = rodio::DeviceSinkBuilder::open_default_sink()?;
45 let file = std::fs::File::open(path)?;
46 let player = rodio::play(&handle.mixer(), std::io::BufReader::new(file))?;
47 player.sleep_until_end();
48 Ok(())
49}
50
51fn play_default_async(event: SoundEvent) {
54 thread::spawn(move || {
55 let _ = play_default_blocking(event);
56 });
57}
58
59fn play_default_blocking(event: SoundEvent) -> Result<(), Box<dyn std::error::Error>> {
60 use rodio::source::{SineWave, Source};
61
62 let mut handle = rodio::DeviceSinkBuilder::open_default_sink()?;
63 handle.log_on_drop(false); let play_tone = |freq: f32, ms: u64, vol: f32| {
67 let src = SineWave::new(freq).take_duration(Duration::from_millis(ms)).amplify(vol);
68 handle.mixer().add(src);
69 std::thread::sleep(Duration::from_millis(ms + 20));
70 };
71
72 match event {
73 SoundEvent::SyncStart => play_tone(660.0, 180, 0.4),
74 SoundEvent::SyncDone => {
75 play_tone(523.0, 120, 0.4);
76 play_tone(784.0, 200, 0.4);
77 }
78 SoundEvent::SyncError => {
79 play_tone(440.0, 120, 0.4);
80 play_tone(220.0, 220, 0.4);
81 }
82 }
83 Ok(())
84}