use serial::SerialPort; use std::io::BufRead; use std::io::BufReader; use std::io::Result as IoResult; use walkdir::WalkDir; fn main() { let helptext = "Argument 1 is serial port (for example /dev/ttyACM0, Argument 2 is the directory with the music, no stars."; let mut args = std::env::args_os(); let _selfname = args.next(); let serialport = args.next().expect(helptext); let musicdir = args.next().expect(helptext); println!("opening serial port"); let mut port = serial::open(&serialport).expect("could not open serial port"); port.reconfigure(&|settings| { settings.set_baud_rate(serial::Baud115200)?; settings.set_char_size(serial::Bits8); settings.set_parity(serial::ParityNone); settings.set_stop_bits(serial::Stop1); settings.set_flow_control(serial::FlowNone); Ok(()) }) .expect("could not configure port"); port.set_timeout(std::time::Duration::from_secs(30)) .expect("could not set timeout"); println!("serial port opened"); println!("skimming music dir"); let walker = WalkDir::new(musicdir).into_iter(); let walker = walker.filter_entry(|e| { e.file_name() .to_str() .map(|s| !s.starts_with('.')) .unwrap_or(true) }); let mut music = Vec::new(); for entry in walker { match entry { Err(e) => eprintln!("error when reading music dir, continuing: {:?}", e), Ok(entry) => { if entry.file_type().is_file() && entry.path().extension() != Some(std::ffi::OsStr::new("jpg")) { music.push(Some(entry.path().to_owned())); } } } } println!("music dir skimmed"); use rand::seq::SliceRandom; use rand::SeedableRng; let mut rng = rand::rngs::SmallRng::seed_from_u64(0); music.shuffle(&mut rng); println!("loaded {} \"songs\"", music.len()); println!("checked all songs"); println!("opening audio output"); let (_stream, stream_handle) = rodio::OutputStream::try_default() .expect("could not open audio output, or could not find any"); let mut sink = rodio::Sink::try_new(&stream_handle).expect("could not create sink"); println!("audio output opened"); #[derive(Clone, Copy, Debug)] struct Hyst { max: usize, prechoice: usize, /// value when the choice was last changed prechoice_val: usize, /// value of last read preval: usize, /// number of times the same (but changed) value has been read stability: usize, } let equality_range = 1; let stability_tresh = 7; let hyst = Hyst { // wage has valid range 0-5000 max: 5000, prechoice: 0, prechoice_val: 0, preval: 0, stability: 0, }; let musiclen = music.len(); let read_delay = std::time::Duration::from_millis(100); // first remove noise let iter = PortIter::new(port, read_delay) .unwrap() .map(|e| e.unwrap()) .scan(hyst, |hyst, el| { // 8888 is empty screen, 69696969 is EEEE if el == 8888 || el == 69_69_69_69 { return Some(None) } hyst.max = hyst.max.max(el); let absdiff = el.max(hyst.prechoice_val) - el.min(hyst.prechoice_val); if absdiff > equality_range { // build stability if value has not changed since last time if hyst.preval == el { hyst.stability += 1; } else { hyst.stability = 0; } hyst.preval = el; // once we are stable if hyst.stability > stability_tresh { let choice = (el * (musiclen - 1)) / hyst.max; // make sure the choice actually changed if choice != hyst.prechoice { hyst.prechoice = choice; hyst.stability = 0; hyst.prechoice_val = el; println!("pushing choice {}", choice); return Some(Some((el, hyst.max))); } } } Some(None) }); // next: turn de-noised into a decoder let iter = iter // skip thing the denoiser denoised .flatten() .map(|(e, max)| { let decoder; // re-run until we hit actually valid music files loop { let base = (e * (music.len() - 1)) / max; let mut delta: isize = 0; let choicef; let choice; // search for non-deleted paths in a cyclic pattern around base choice // (0, -1, 1, -2, 2...) loop { let modchoice = if delta > 0 { let c = base + (delta as usize); delta = -delta; c } else { let c = base - (delta as usize); delta = (-delta) + 1; c }; println!("trying {} next delta {}", modchoice, delta); if let Some(Some(c)) = music.get(modchoice) { choicef = c; choice = modchoice; break; } } // try to decode let c = || -> Result, ()> { let f = std::fs::File::open(choicef).map_err(|_| ())?; let rdr = BufReader::new(f); rodio::Decoder::new(rdr).map_err(|_| ()) }; match (c)() { Ok(d) => { decoder = d; println!("playing {:?}", choicef); break; } Err(()) => { eprintln!("{:?} was not actually music", choicef); music[choice] = None; } } } decoder }); // now just play the decoder for decoder in iter { let newsink = rodio::Sink::try_new(&stream_handle).expect("could not create sink"); newsink.append(decoder); sink.stop(); sink = newsink; } } #[derive(Debug)] enum PortIterErr { Io(std::io::Error), Parse(std::num::ParseIntError), } impl From for PortIterErr { fn from(e: std::io::Error) -> Self { Self::Io(e) } } impl From for PortIterErr { fn from(e: std::num::ParseIntError) -> Self { Self::Parse(e) } } struct PortIter { port: BufReader, delay: std::time::Duration, } impl PortIter { fn new(mut p: S, delay: std::time::Duration) -> IoResult { p.write_all(b"r")?; Ok(Self { port: BufReader::new(p), delay, }) } } impl Iterator for PortIter { type Item = Result; fn next(&mut self) -> Option> { let mut buf = String::new(); let r = self.port.read_line(&mut buf); match r { Ok(0) => return None, Err(e) => return Some(Result::Err(e.into())), Ok(_) => (), }; let num = match buf.trim().parse::() { Err(e) => return Some(Result::Err(e.into())), Ok(num) => num, }; match self.port.get_mut().write_all(b"r") { Ok(_) => (), Err(e) => return Some(Result::Err(e.into())), } std::thread::sleep(self.delay); Some(Result::Ok(num)) } }