2021-02-16 22:44:31 +01:00
|
|
|
// vim: noet
|
|
|
|
|
|
|
|
use std::process::exit;
|
2021-02-19 23:26:28 +01:00
|
|
|
use std::collections::VecDeque;
|
2021-02-16 22:44:31 +01:00
|
|
|
|
|
|
|
use byteorder::{NativeEndian, ReadBytesExt};
|
|
|
|
|
|
|
|
mod signal_processing;
|
|
|
|
mod config;
|
2021-02-22 23:24:32 +01:00
|
|
|
mod udpproto;
|
2021-03-07 20:32:34 +01:00
|
|
|
mod animation;
|
2021-02-16 22:44:31 +01:00
|
|
|
|
|
|
|
use crate::signal_processing::SignalProcessing;
|
2021-02-22 23:24:32 +01:00
|
|
|
use crate::udpproto::UdpProto;
|
2021-03-07 20:32:34 +01:00
|
|
|
use crate::animation::Animation;
|
2021-02-16 22:44:31 +01:00
|
|
|
|
2021-02-20 21:47:30 +01:00
|
|
|
use std::rc::Rc;
|
|
|
|
use std::cell::RefCell;
|
|
|
|
|
2021-02-23 22:32:34 +01:00
|
|
|
use std::thread::sleep;
|
|
|
|
use std::time::{Duration, Instant};
|
|
|
|
|
2021-02-16 22:44:31 +01:00
|
|
|
fn main()
|
|
|
|
{
|
|
|
|
let mut stdin = std::io::stdin();
|
|
|
|
|
2021-02-22 23:24:32 +01:00
|
|
|
// set up the UDP protocol
|
|
|
|
let mut udpproto = match UdpProto::new(config::UDP_SERVER_ADDR) {
|
|
|
|
Ok(u) => u,
|
|
|
|
Err(e) => {
|
|
|
|
println!("Error during UDP client setup:\n{}", e);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-20 21:47:30 +01:00
|
|
|
// set up signal processing
|
|
|
|
|
|
|
|
println!("Initializing signal processing...");
|
|
|
|
|
|
|
|
let sigproc = Rc::new(RefCell::new(
|
|
|
|
SignalProcessing::new(config::BLOCK_LEN, config::SAMP_RATE).unwrap()));
|
|
|
|
|
2021-03-07 20:32:34 +01:00
|
|
|
println!("Contructing Animation...");
|
|
|
|
|
2021-04-09 17:38:42 +02:00
|
|
|
// TODO: let the user select via the command line
|
|
|
|
//let mut anim: animation::particles::Particles = animation::Animation::new(sigproc.clone());
|
2022-03-19 23:46:55 +01:00
|
|
|
//let mut anim: animation::sparkles::Sparkles = animation::Animation::new(sigproc.clone());
|
|
|
|
let mut anim: animation::racers::Racers = animation::Animation::new(sigproc.clone());
|
2021-03-07 20:32:34 +01:00
|
|
|
|
|
|
|
println!("Calling Animation::init()...");
|
|
|
|
|
|
|
|
anim.init().unwrap();
|
2021-02-16 22:44:31 +01:00
|
|
|
|
|
|
|
println!("Done! Starting main loop…");
|
|
|
|
|
2021-02-23 22:32:34 +01:00
|
|
|
// Timing setup
|
|
|
|
|
|
|
|
let block_period = Duration::from_nanos((0.95 * (config::SAMPLES_PER_UPDATE as f32) * 1e9 / config::SAMP_RATE) as u64);
|
2021-04-09 17:38:42 +02:00
|
|
|
let send_period = Duration::from_nanos((1000000000.0 / config::FPS_LEDS) as u64);
|
2021-02-23 22:32:34 +01:00
|
|
|
|
2021-04-08 20:49:32 +02:00
|
|
|
let max_lag = 5*send_period;
|
2021-04-04 19:59:46 +02:00
|
|
|
|
2021-02-23 22:32:34 +01:00
|
|
|
let mut next_block_instant = Instant::now() + block_period;
|
|
|
|
let mut next_send_instant = Instant::now() + send_period;
|
|
|
|
|
2021-02-16 22:44:31 +01:00
|
|
|
// array for samples directly read from stream
|
2021-02-19 23:26:28 +01:00
|
|
|
let mut samples: VecDeque<i16> = VecDeque::with_capacity(config::BLOCK_LEN);
|
2021-02-16 22:44:31 +01:00
|
|
|
|
2021-06-27 22:01:15 +02:00
|
|
|
// counts silent (zero-valued) samples
|
|
|
|
let mut silent_samples: usize = 0;
|
|
|
|
|
2021-02-16 22:44:31 +01:00
|
|
|
// main loop
|
|
|
|
loop {
|
|
|
|
|
|
|
|
// read a block of samples and exit gracefully on EOF
|
2021-02-19 23:26:28 +01:00
|
|
|
for _i in 0 .. config::SAMPLES_PER_UPDATE {
|
|
|
|
// avoid increasing the size of the deque
|
|
|
|
if samples.len() == config::BLOCK_LEN {
|
|
|
|
samples.pop_front();
|
|
|
|
}
|
|
|
|
|
|
|
|
// read a sample from the input
|
2021-02-16 22:44:31 +01:00
|
|
|
let res = stdin.read_i16::<NativeEndian>();
|
|
|
|
|
2021-02-19 23:26:28 +01:00
|
|
|
// if everything is ok, append it to the samples deque
|
2021-02-16 22:44:31 +01:00
|
|
|
match res {
|
2021-02-19 23:26:28 +01:00
|
|
|
Ok(s) => samples.push_back(s),
|
2021-02-16 22:44:31 +01:00
|
|
|
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
|
|
|
|
println!("End of stream. Exiting.");
|
|
|
|
exit(0);
|
|
|
|
},
|
|
|
|
Err(e) => panic!(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-19 23:26:28 +01:00
|
|
|
// only run calculations if the deque has been filled enough
|
|
|
|
if samples.len() < config::BLOCK_LEN {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-02-21 19:45:03 +01:00
|
|
|
// run the signal processing
|
2021-02-20 21:47:30 +01:00
|
|
|
{
|
|
|
|
let mut s = sigproc.borrow_mut();
|
|
|
|
s.import_i16_mono_from_iter(samples.iter()).unwrap();
|
2021-06-27 22:01:15 +02:00
|
|
|
|
|
|
|
if s.is_silent() {
|
|
|
|
silent_samples += config::BLOCK_LEN;
|
|
|
|
|
|
|
|
if silent_samples >= config::STANDBY_MAX_SILENT_SAMPLES {
|
|
|
|
// too many silent samples in a row: stop any signal processing until something
|
|
|
|
// else occurs at the input again
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
silent_samples = 0;
|
|
|
|
}
|
|
|
|
|
2021-02-20 21:47:30 +01:00
|
|
|
s.update_fft().unwrap();
|
|
|
|
}
|
2021-02-16 22:44:31 +01:00
|
|
|
|
2021-02-19 23:29:52 +01:00
|
|
|
// call the periodic function in the user script
|
2021-03-07 20:32:34 +01:00
|
|
|
match anim.periodic() {
|
2021-02-21 19:41:45 +01:00
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) => {
|
2021-03-07 20:32:34 +01:00
|
|
|
println!("=== Animation Error ===\n{}\n====> Terminating.", e);
|
2021-02-21 19:41:45 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-23 22:32:34 +01:00
|
|
|
if Instant::now() > next_send_instant {
|
2021-03-07 20:32:34 +01:00
|
|
|
let colorlists = anim.get_colorlist();
|
|
|
|
|
|
|
|
for i in 0..config::NUM_LEDS_TOTAL {
|
|
|
|
let strip = i / config::NUM_LEDS_PER_STRIP;
|
|
|
|
let led = i % config::NUM_LEDS_PER_STRIP;
|
|
|
|
|
2021-03-10 22:38:30 +01:00
|
|
|
let r = udpproto.set_color(strip as u8,
|
|
|
|
led as u8,
|
|
|
|
(colorlists[strip][led].r * 255.0) as u8,
|
|
|
|
(colorlists[strip][led].g * 255.0) as u8,
|
|
|
|
(colorlists[strip][led].b * 255.0) as u8,
|
|
|
|
(colorlists[strip][led].w * 255.0) as u8);
|
|
|
|
|
|
|
|
match r {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) if e.kind() == std::io::ErrorKind::ConnectionRefused => {
|
|
|
|
// try again in one second
|
|
|
|
next_send_instant += Duration::from_secs(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Err(e) => panic!(e),
|
|
|
|
}
|
2021-02-23 22:32:34 +01:00
|
|
|
}
|
|
|
|
|
2021-03-10 22:38:30 +01:00
|
|
|
match udpproto.commit() {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(e) if e.kind() == std::io::ErrorKind::ConnectionRefused => {
|
|
|
|
// try again in one second
|
|
|
|
next_send_instant += Duration::from_secs(1);
|
|
|
|
}
|
|
|
|
Err(e) => panic!(e),
|
|
|
|
}
|
2021-02-23 22:32:34 +01:00
|
|
|
|
2021-04-04 19:59:46 +02:00
|
|
|
let now = Instant::now();
|
2021-04-08 20:49:32 +02:00
|
|
|
if now > (next_send_instant + max_lag) {
|
2021-04-04 19:59:46 +02:00
|
|
|
println!("Warning! Lag exceeds {:?}. Resetting sender timing.", max_lag);
|
|
|
|
next_send_instant = now + send_period;
|
|
|
|
} else {
|
|
|
|
next_send_instant += send_period;
|
|
|
|
}
|
2021-02-23 22:32:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
let now = Instant::now();
|
|
|
|
if now < next_block_instant {
|
|
|
|
sleep(next_block_instant - now);
|
2021-02-22 23:24:32 +01:00
|
|
|
}
|
|
|
|
|
2021-02-23 22:32:34 +01:00
|
|
|
next_block_instant = Instant::now() + block_period;
|
2021-02-16 22:44:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|