Update
This commit is contained in:
24
beat_detection/src/layers/constant_brightness.rs
Normal file
24
beat_detection/src/layers/constant_brightness.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use super::OutputLayer;
|
||||
|
||||
pub struct ConstantBrightness {
|
||||
pub brightness: f32,
|
||||
}
|
||||
|
||||
impl OutputLayer for ConstantBrightness {
|
||||
fn tick(&mut self, _dt: std::time::Duration) {}
|
||||
|
||||
fn draw_sdl(
|
||||
&self,
|
||||
_canvas: &mut sdl2::render::Canvas<sdl2::video::Window>,
|
||||
_texture_size: u32,
|
||||
) {
|
||||
}
|
||||
|
||||
fn on_sdl_event(&mut self, _event: &sdl2::event::Event) {}
|
||||
|
||||
fn update_dmx(&self, output: &mut [crate::fixtures::MovingHead; 4]) {
|
||||
for head in output.iter_mut() {
|
||||
head.dimmer = self.brightness;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ impl OutputLayer for HSLCycle {
|
||||
0.5,
|
||||
)
|
||||
.into_color();
|
||||
|
||||
|
||||
self.rgb = hsl.into_format().into_raw();
|
||||
}
|
||||
|
||||
@@ -42,9 +42,7 @@ impl OutputLayer for HSLCycle {
|
||||
canvas.fill_rect(Rect::new(0, 0, 5, 5)).unwrap();
|
||||
}
|
||||
|
||||
fn on_sdl_event(&mut self, _event: &sdl2::event::Event) {
|
||||
|
||||
}
|
||||
fn on_sdl_event(&mut self, _event: &sdl2::event::Event) {}
|
||||
|
||||
fn update_dmx(&self, output: &mut [crate::fixtures::MovingHead; 4]) {
|
||||
let [r, g, b] = self.rgb;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
pub mod hsl_cycle;
|
||||
pub mod beat_detector;
|
||||
pub mod constant_brightness;
|
||||
pub mod hsl_cycle;
|
||||
pub mod movement;
|
||||
pub mod tap;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -11,8 +14,8 @@ pub trait OutputLayer {
|
||||
fn tick(&mut self, dt: Duration);
|
||||
|
||||
fn draw_sdl(&self, canvas: &mut Canvas<Window>, texture_size: u32);
|
||||
|
||||
|
||||
fn on_sdl_event(&mut self, event: &Event);
|
||||
|
||||
fn update_dmx(&self, output: &mut [MovingHead; 4]);
|
||||
}
|
||||
}
|
||||
|
||||
78
beat_detection/src/layers/movement.rs
Normal file
78
beat_detection/src/layers/movement.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use std::{
|
||||
f32::consts::PI,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use rand::random;
|
||||
use sdl2::{event::Event, keyboard::Keycode};
|
||||
|
||||
use crate::util::rescale;
|
||||
|
||||
use super::OutputLayer;
|
||||
|
||||
pub struct HeadMover {
|
||||
movement_duration: Duration,
|
||||
last_timestamp: Instant,
|
||||
targets: [(f32, f32); 4],
|
||||
}
|
||||
|
||||
impl HeadMover {
|
||||
pub fn new(movement_duration: Duration) -> Self {
|
||||
Self {
|
||||
movement_duration,
|
||||
last_timestamp: Instant::now() - movement_duration,
|
||||
targets: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_random_point() -> (f32, f32) {
|
||||
let pan = rescale(random::<f32>(), (0.0, 1.0), (-0.5 * PI, 0.5 * PI));
|
||||
let tilt = random::<f32>().acos();
|
||||
|
||||
(pan, tilt)
|
||||
}
|
||||
|
||||
impl OutputLayer for HeadMover {
|
||||
fn tick(&mut self, _dt: Duration) {
|
||||
let now = Instant::now();
|
||||
if now - self.last_timestamp >= self.movement_duration {
|
||||
for target in self.targets.iter_mut() {
|
||||
*target = sample_random_point();
|
||||
}
|
||||
self.last_timestamp = now;
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_sdl(
|
||||
&self,
|
||||
_canvas: &mut sdl2::render::Canvas<sdl2::video::Window>,
|
||||
_texture_size: u32,
|
||||
) {
|
||||
}
|
||||
|
||||
fn on_sdl_event(&mut self, event: &sdl2::event::Event) {
|
||||
match event {
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::Return),
|
||||
..
|
||||
} => {
|
||||
let now = Instant::now();
|
||||
for target in self.targets.iter_mut() {
|
||||
*target = sample_random_point();
|
||||
}
|
||||
self.last_timestamp = now;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_dmx(&self, output: &mut [crate::fixtures::MovingHead; 4]) {
|
||||
for (head, target) in output.iter_mut().zip(self.targets.iter()) {
|
||||
let (pan, tilt) = target;
|
||||
head.pan = *pan;
|
||||
head.tilt = *tilt;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
beat_detection/src/layers/tap.rs
Normal file
102
beat_detection/src/layers/tap.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use sdl2::{event::Event, keyboard::Keycode, pixels::Color};
|
||||
|
||||
use super::OutputLayer;
|
||||
|
||||
pub struct TapMetronome {
|
||||
max_time: Duration,
|
||||
timestamps: VecDeque<Instant>,
|
||||
seconds_per_beat: Option<f32>,
|
||||
brightness: f32,
|
||||
decay: f32,
|
||||
}
|
||||
|
||||
impl TapMetronome {
|
||||
pub fn new(max_time: Duration) -> Self {
|
||||
Self {
|
||||
max_time,
|
||||
timestamps: VecDeque::new(),
|
||||
seconds_per_beat: None,
|
||||
brightness: 0.0,
|
||||
decay: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputLayer for TapMetronome {
|
||||
fn tick(&mut self, _dt: std::time::Duration) {
|
||||
let now = Instant::now();
|
||||
if let (Some(stamp), Some(spb)) = (self.timestamps.back(), self.seconds_per_beat) {
|
||||
let dt = (now - *stamp).as_secs_f32();
|
||||
|
||||
let beat_offset = dt % spb;
|
||||
|
||||
self.brightness = (-1.0 * self.decay * beat_offset).exp()
|
||||
} else {
|
||||
self.brightness = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_sdl(&self, canvas: &mut sdl2::render::Canvas<sdl2::video::Window>, _texture_size: u32) {
|
||||
let v = (255.0 * self.brightness) as u8;
|
||||
canvas.set_draw_color(Color::RGB(v, v, v));
|
||||
canvas.clear();
|
||||
}
|
||||
|
||||
fn on_sdl_event(&mut self, event: &sdl2::event::Event) {
|
||||
match event {
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::Space),
|
||||
..
|
||||
} => {
|
||||
let now = Instant::now();
|
||||
while let Some(stamp) = self.timestamps.front() {
|
||||
if now - *stamp < self.max_time {
|
||||
break;
|
||||
}
|
||||
self.timestamps.pop_front();
|
||||
}
|
||||
self.timestamps.push_back(now);
|
||||
|
||||
if self.timestamps.len() >= 2 {
|
||||
let dt = *self.timestamps.back().unwrap() - *self.timestamps.front().unwrap();
|
||||
let spb = dt.as_secs_f32() / (self.timestamps.len() - 1) as f32;
|
||||
|
||||
println!("Detected BPM: {:.2}", 60.0 / spb);
|
||||
|
||||
self.seconds_per_beat = Some(spb);
|
||||
} else {
|
||||
self.seconds_per_beat = None;
|
||||
}
|
||||
}
|
||||
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::Down),
|
||||
..
|
||||
} => {
|
||||
self.decay += 0.1;
|
||||
println!("Decay: {:.3}", self.decay)
|
||||
}
|
||||
|
||||
Event::KeyDown {
|
||||
keycode: Some(Keycode::Up),
|
||||
..
|
||||
} => {
|
||||
self.decay -= 0.1;
|
||||
println!("Decay: {:.3}", self.decay)
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_dmx(&self, output: &mut [crate::fixtures::MovingHead; 4]) {
|
||||
for head in output.iter_mut() {
|
||||
head.dimmer = 0.2 + 0.8 * self.brightness;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user