use rodio::source::{SineWave, Source};
use rodio::{OutputStream, OutputStreamHandle, Sink};
use simple_logger::SimpleLogger;
use std::process;
use std::time::Duration;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
struct Opt {
#[structopt(short, default_value = "80")]
beats_per_minute: u32,
#[structopt(short = "a", default_value = "4")]
beats_per_bar: u8,
}
fn main() {
SimpleLogger::new().init().unwrap();
let (_stream, stream_handle) = OutputStream::try_default().unwrap_or_else(|error| {
log::error!("Cannot open output stream.\n More info: {}", error);
process::exit(1);
});
run(Opt::from_args(), &stream_handle);
}
fn play_sound(stream_handle: &OutputStreamHandle, is_accent: bool) {
let low_pitch = 440;
let amplification = 0.20;
let beep_duration = 0.050;
let sink = Sink::try_new(stream_handle).unwrap_or_else(|error| {
log::error!("Cannot open sink stream!\n More info: {}", error);
process::exit(1);
});
let small_hertz_leap = 40;
let medium_pitch = low_pitch + small_hertz_leap;
let source = SineWave::new(match is_accent {
true => medium_pitch,
_ => low_pitch,
})
.take_duration(Duration::from_secs_f32(beep_duration))
.amplify(amplification);
sink.append(source);
sink.sleep_until_end();
}
fn is_bpm_valid(opt: &Opt) -> bool {
let minimum = 40;
let maximum = 180;
opt.beats_per_minute >= minimum && opt.beats_per_minute <= maximum
}
fn is_bpb_valid(opt: &Opt) -> bool {
let minimum = 2;
let maximum = 4;
opt.beats_per_bar >= minimum && opt.beats_per_bar <= maximum
}
fn run(opt: Opt, stream_handle: &OutputStreamHandle) {
if !is_bpm_valid(&opt) {
log::error!("Beats per minute must be in range of 40 to 180");
process::exit(1);
}
if !is_bpb_valid(&opt) {
log::error!("Beats per bar must be in range of 2 to 4");
process::exit(1);
}
let minute = 60.0;
let duration = minute / opt.beats_per_minute as f32;
let mut counter: u8 = 0;
println!("Ctrl+C to quit");
loop {
counter += 1;
if counter % opt.beats_per_bar as u8 != 0 {
play_sound(stream_handle, false);
} else {
play_sound(stream_handle, true);
counter = 0;
}
std::thread::sleep(Duration::from_secs_f32(duration));
}
}