diff --git a/src/main.rs b/src/main.rs index 5155b3c..19cbcdb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,7 @@ use serial::SerialPort; +use std::io::BufRead; +use std::io::BufReader; +use std::io::Result as IoResult; use walkdir::WalkDir; fn main() { @@ -39,8 +42,10 @@ fn main() { 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(entry.path().to_owned()); + if entry.file_type().is_file() + && entry.path().extension() != Some(std::ffi::OsStr::new("jpg")) + { + music.push(Some(entry.path().to_owned())); } } } @@ -52,27 +57,6 @@ fn main() { music.shuffle(&mut rng); println!("loaded {} \"songs\"", music.len()); - /* - for i in (0..music.len()).rev() { - enum Err{ - Empty, - File, - } - let c = || -> Result<(), Err> { - // we might run out of music cause we are deliting inline, this is fine. - let choice = &music.get(i).ok_or(Err::Empty)?; - - let f = std::fs::File::open(&choice).map_err(|_| Err::File)?; - let rdr = BufReader::new(f); - let _d = rodio::Decoder::new(rdr).map_err(|_| Err::File)?; - Ok(()) - }; - match (c)() { - Err(Err::File) => {dbg!(music.remove(i));}, - _ => (), - } - } - */ println!("checked all songs"); println!("opening audio output"); let (_stream, stream_handle) = rodio::OutputStream::try_default() @@ -80,67 +64,168 @@ fn main() { let mut sink = rodio::Sink::try_new(&stream_handle).expect("could not create sink"); println!("audio output opened"); - use std::io::BufRead; - use std::io::BufReader; - let mut port = BufReader::new(port); + #[derive(Clone, Copy)] + 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, + } - // wage has valid range 0-5000 - let mut max: usize = 5000; - let mut preval = 0; - let mut prechoice = 0; - let mut readbuf = String::new(); - let mut n_changed = 0; + let equality_range = 1; + let stability_tresh = 7; - use std::io::Write; - port.get_mut().write(b"r").unwrap(); + let hyst = Hyst { + // wage has valid range 0-5000 + max: 5000, + prechoice: 0, + prechoice_val: 0, + preval: 0, + stability: 0, + }; - loop { - readbuf.clear(); - port.read_line(&mut readbuf).unwrap(); - //println!("got line: {}", readbuf); - let err = "got invalid message from wage"; - let w: usize = readbuf.trim().parse().expect(err); - if w == 8888 || w == 69_69_69_69 { - // special values: 8888 and 69696969 - // are turned off and EEEE - } else { - max = max.max(w); - let choice = (w * (music.len() - 1)) / max; - let absdiff = w.max(preval) - w.min(preval); - if absdiff > 1 && choice != prechoice { - n_changed += 1; - } else { - n_changed = 0; - } - if n_changed >= 7 { - let choicef = &music[(music.len() - 1) - choice]; - println!("changing song to {:?}", choicef); - let mut c = || -> Result<(), ()> { - let f = std::fs::File::open(choicef).map_err(|_| ())?; - let rdr = BufReader::new(f); - let d = rodio::Decoder::new(rdr).map_err(|_| ())?; - //let s = rodio::source::SineWave::new(440); - let newsink = - rodio::Sink::try_new(&stream_handle).expect("could not create sink"); - newsink.append(d); + let musiclen = music.len(); - sink.stop(); - sink = newsink; - Ok(()) - }; - if let Err(_error) = (c)() { - println!("music was not music"); - let r = music.remove((music.len() - 1) - choice); - println!("removed {:?}", r); + 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| { + hyst.max = hyst.max.max(el); + let absdiff = el.max(hyst.prechoice_val) - el.min(hyst.prechoice_val); + if absdiff > equality_range { + if hyst.preval == el { + hyst.stability += 1; } else { - preval = w; - prechoice = choice; - n_changed = 0; + hyst.stability = 0; + } + if hyst.stability > stability_tresh { + let choice = (el * (musiclen - 1)) / hyst.max; + if choice != hyst.prechoice { + hyst.prechoice = choice; + hyst.stability = 0; + hyst.preval = el; + 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 + }; + 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; + 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); - std::thread::sleep(std::time::Duration::from_millis(100)); - port.get_mut().write(b"r").unwrap(); + 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.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)) } }