Refactor
This commit is contained in:
parent
9f07172f60
commit
30a4f83d32
@ -11,6 +11,7 @@ const FPS: u32 = 50;
|
|||||||
enum MCUResponse {
|
enum MCUResponse {
|
||||||
Sync,
|
Sync,
|
||||||
Ack,
|
Ack,
|
||||||
|
#[allow(dead_code)]
|
||||||
Info { num_pkts: u32 },
|
Info { num_pkts: u32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,15 +39,13 @@ fn poll_response(ser: &mut dyn SerialPort) -> Result<MCUResponse> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn controller_thread(running: Arc<Mutex<bool>>, brightness: Arc<Mutex<f32>>) -> Result<()> {
|
pub fn controller_thread(running: Arc<Mutex<bool>>, movingheads: Arc<Mutex<[MovingHead; 4]>>) -> Result<()> {
|
||||||
let frame_time = Duration::from_secs_f64(1.0 / FPS as f64);
|
let frame_time = Duration::from_secs_f64(1.0 / FPS as f64);
|
||||||
|
|
||||||
let hsl_cycle = 12 * FPS;
|
let hsl_cycle = 12 * FPS;
|
||||||
|
|
||||||
let mut dmx_buffer = [0u8; 512];
|
let mut dmx_buffer = [0u8; 512];
|
||||||
|
|
||||||
let mut movinghead = MovingHead::new(1);
|
|
||||||
|
|
||||||
let mut ser = serialport::new("/dev/ttyUSB0", 500_000)
|
let mut ser = serialport::new("/dev/ttyUSB0", 500_000)
|
||||||
.timeout(Duration::from_millis(10))
|
.timeout(Duration::from_millis(10))
|
||||||
.open()?;
|
.open()?;
|
||||||
@ -72,18 +71,12 @@ pub fn controller_thread(running: Arc<Mutex<bool>>, brightness: Arc<Mutex<f32>>)
|
|||||||
|
|
||||||
let loop_start = Instant::now();
|
let loop_start = Instant::now();
|
||||||
|
|
||||||
let hsl: Srgb = Hsl::new(360.0 * (t as f32 / hsl_cycle as f32), 1.0, 0.5).into_color();
|
{
|
||||||
|
let movingheads = movingheads.lock().unwrap();
|
||||||
let [r, g, b]: [u8; 3] = hsl.into_format().into_raw();
|
for head in movingheads.iter() {
|
||||||
movinghead.rgbw = (r, g, b, 0);
|
head.render(&mut dmx_buffer);
|
||||||
|
}
|
||||||
movinghead.dimmer = {
|
}
|
||||||
let brightness = brightness.lock().unwrap();
|
|
||||||
|
|
||||||
0.2 + 0.8 * *brightness
|
|
||||||
};
|
|
||||||
|
|
||||||
movinghead.render(&mut dmx_buffer);
|
|
||||||
|
|
||||||
t += 1;
|
t += 1;
|
||||||
t %= hsl_cycle;
|
t %= hsl_cycle;
|
||||||
|
288
beat_detection/src/layers/beat_detector.rs
Normal file
288
beat_detection/src/layers/beat_detector.rs
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
use std::{
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use pulse::sample::{Format, Spec};
|
||||||
|
use ringbuffer::{ConstGenericRingBuffer, RingBufferExt, RingBufferWrite};
|
||||||
|
use sdl2::{
|
||||||
|
event::Event,
|
||||||
|
keyboard::Keycode,
|
||||||
|
pixels::Color,
|
||||||
|
rect::{Point, Rect},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
capture,
|
||||||
|
dsp::{self, ZTransformFilter},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::OutputLayer;
|
||||||
|
|
||||||
|
const SAMPLE_RATE: usize = 5000;
|
||||||
|
const PULSE_UPDATES_PER_SECOND: usize = 50;
|
||||||
|
const BUFFER_SIZE: usize = SAMPLE_RATE / PULSE_UPDATES_PER_SECOND;
|
||||||
|
|
||||||
|
const POINT_COUNT: usize = SAMPLE_RATE / 200;
|
||||||
|
const POINT_BUFFER_SIZE: usize = POINT_COUNT.next_power_of_two();
|
||||||
|
|
||||||
|
const MIN_MAX_SAMPLE_COUNT: usize = 10;
|
||||||
|
|
||||||
|
pub struct BeatDetectinator {
|
||||||
|
join_handle: Option<JoinHandle<Result<()>>>,
|
||||||
|
shared_state: Arc<Mutex<BeatDetectinatorSharedState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BeatDetectinatorSharedState {
|
||||||
|
running: bool,
|
||||||
|
brightness: f32,
|
||||||
|
threshold: f32,
|
||||||
|
point_buf: ConstGenericRingBuffer<f32, POINT_BUFFER_SIZE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BeatDetectinator {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let shared_state = Arc::new(Mutex::new(BeatDetectinatorSharedState {
|
||||||
|
running: true,
|
||||||
|
brightness: 0.0,
|
||||||
|
threshold: 1.5,
|
||||||
|
point_buf: Default::default(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
{
|
||||||
|
shared_state.lock().unwrap().point_buf.fill_default();
|
||||||
|
}
|
||||||
|
|
||||||
|
let join_handle = {
|
||||||
|
let shared_state = shared_state.clone();
|
||||||
|
Some(thread::spawn(move || audio_loop(shared_state)))
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Audio thread started.");
|
||||||
|
|
||||||
|
Self {
|
||||||
|
join_handle,
|
||||||
|
shared_state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for BeatDetectinator {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
{
|
||||||
|
self.shared_state.lock().unwrap().running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.join_handle.take().unwrap().join().unwrap() {
|
||||||
|
Ok(_) => println!("Audio thread stopped."),
|
||||||
|
Err(e) => println!("Audio thread died: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn audio_loop(shared_state: Arc<Mutex<BeatDetectinatorSharedState>>) -> Result<()> {
|
||||||
|
let spec = Spec {
|
||||||
|
format: Format::F32le,
|
||||||
|
rate: SAMPLE_RATE as u32,
|
||||||
|
channels: 1,
|
||||||
|
};
|
||||||
|
assert!(spec.is_valid());
|
||||||
|
|
||||||
|
let reader = capture::get_audio_reader(&spec)?;
|
||||||
|
let mut buffer = [0u8; 4 * BUFFER_SIZE];
|
||||||
|
|
||||||
|
let mut bass_filter = dsp::BassFilter::default();
|
||||||
|
let mut envelope_filter = dsp::EnvelopeFilter::default();
|
||||||
|
let mut beat_filter = dsp::BeatFilter::default();
|
||||||
|
let mut j = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
{
|
||||||
|
if shared_state.lock().unwrap().running == false {
|
||||||
|
break Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.read(&mut buffer)?;
|
||||||
|
|
||||||
|
for i in 0..BUFFER_SIZE {
|
||||||
|
let mut float_bytes = [0u8; 4];
|
||||||
|
float_bytes.copy_from_slice(&buffer[4 * i..4 * i + 4]);
|
||||||
|
|
||||||
|
j += 1;
|
||||||
|
let sample = f32::from_le_bytes(float_bytes);
|
||||||
|
let mut value = bass_filter.process(sample);
|
||||||
|
|
||||||
|
if value < 0f32 {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
|
||||||
|
let envelope = envelope_filter.process(value);
|
||||||
|
|
||||||
|
if j == 200 {
|
||||||
|
let beat = beat_filter.process(envelope);
|
||||||
|
|
||||||
|
shared_state.lock().unwrap().point_buf.push(beat);
|
||||||
|
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputLayer for BeatDetectinator {
|
||||||
|
fn tick(&mut self, _dt: std::time::Duration) {}
|
||||||
|
|
||||||
|
fn draw_sdl(&self, canvas: &mut sdl2::render::Canvas<sdl2::video::Window>, texture_size: u32) {
|
||||||
|
let min_y = 0f32;
|
||||||
|
let max_y = 3f32;
|
||||||
|
|
||||||
|
let beat = {
|
||||||
|
self.shared_state
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.point_buf
|
||||||
|
.back()
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let get_y = |y: &f32| {
|
||||||
|
let mut y = y.clone();
|
||||||
|
|
||||||
|
if y <= 0f32 {
|
||||||
|
y = 0f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
y = (1f32 + y).log2();
|
||||||
|
|
||||||
|
let y = (y - min_y) / (max_y - min_y);
|
||||||
|
|
||||||
|
((1f32 - y) * texture_size as f32) as u32
|
||||||
|
};
|
||||||
|
|
||||||
|
// background
|
||||||
|
let v;
|
||||||
|
let (threshold, points) = {
|
||||||
|
let mut shared_state = self.shared_state.lock().unwrap();
|
||||||
|
shared_state.brightness = if beat > shared_state.threshold {
|
||||||
|
1f32
|
||||||
|
} else {
|
||||||
|
0.75f32 * shared_state.brightness
|
||||||
|
};
|
||||||
|
|
||||||
|
v = (255f32 * shared_state.brightness) as u8;
|
||||||
|
|
||||||
|
(
|
||||||
|
shared_state.threshold.clone(),
|
||||||
|
shared_state.point_buf.to_vec(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
canvas.set_draw_color(Color::RGB(v, v, v));
|
||||||
|
canvas.clear();
|
||||||
|
|
||||||
|
// zero
|
||||||
|
|
||||||
|
canvas.set_draw_color(Color::RGB(0, 128, 0));
|
||||||
|
let y = get_y(&0f32);
|
||||||
|
|
||||||
|
canvas
|
||||||
|
.draw_line(
|
||||||
|
Point::new(0, y as i32),
|
||||||
|
Point::new(texture_size as i32, y as i32),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// min / max lines
|
||||||
|
|
||||||
|
canvas.set_draw_color(Color::RGB(255, 0, 0));
|
||||||
|
|
||||||
|
let min_beat = points
|
||||||
|
.iter()
|
||||||
|
.skip(points.len() - MIN_MAX_SAMPLE_COUNT)
|
||||||
|
.reduce(|a, b| if a < b { a } else { b })
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let x = texture_size - MIN_MAX_SAMPLE_COUNT as u32 * 10;
|
||||||
|
|
||||||
|
let y = get_y(min_beat);
|
||||||
|
|
||||||
|
canvas
|
||||||
|
.draw_line(
|
||||||
|
Point::new(x as i32, y as i32),
|
||||||
|
Point::new(texture_size as i32, y as i32),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let max_beat = points
|
||||||
|
.iter()
|
||||||
|
.skip(points.len() - MIN_MAX_SAMPLE_COUNT)
|
||||||
|
.reduce(|a, b| if a > b { a } else { b })
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let y = get_y(max_beat);
|
||||||
|
|
||||||
|
canvas
|
||||||
|
.draw_line(
|
||||||
|
Point::new(x as i32, y as i32),
|
||||||
|
Point::new(texture_size as i32, y as i32),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// threshhold line
|
||||||
|
|
||||||
|
canvas.set_draw_color(Color::RGB(0, 0, 255));
|
||||||
|
let y = get_y(&threshold);
|
||||||
|
|
||||||
|
canvas
|
||||||
|
.draw_line(
|
||||||
|
Point::new(0, y as i32),
|
||||||
|
Point::new(texture_size as i32, y as i32),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// values
|
||||||
|
|
||||||
|
canvas.set_draw_color(Color::RGB(0, 255, 0));
|
||||||
|
|
||||||
|
for (i, beat) in points.iter().skip(points.len() - POINT_COUNT).enumerate() {
|
||||||
|
let x = 10 * i;
|
||||||
|
let y = get_y(beat);
|
||||||
|
|
||||||
|
canvas
|
||||||
|
.fill_rect(Rect::new((x + 1) as i32, (y - 1) as i32, 8, 3))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_sdl_event(&mut self, event: &Event) {
|
||||||
|
match event {
|
||||||
|
Event::KeyDown {
|
||||||
|
keycode: Some(Keycode::Up),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut shared_state = self.shared_state.lock().unwrap();
|
||||||
|
shared_state.threshold += 0.01f32;
|
||||||
|
println!("threshold: {:.2}", shared_state.threshold);
|
||||||
|
}
|
||||||
|
Event::KeyDown {
|
||||||
|
keycode: Some(Keycode::Down),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut shared_state = self.shared_state.lock().unwrap();
|
||||||
|
shared_state.threshold -= 0.01f32;
|
||||||
|
println!("threshold: {:.2}", shared_state.threshold);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_dmx(&self, output: &mut [crate::fixtures::MovingHead; 4]) {
|
||||||
|
let shared_state = self.shared_state.lock().unwrap();
|
||||||
|
for head in output.iter_mut() {
|
||||||
|
head.dimmer = 0.2 + 0.8 * shared_state.brightness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
beat_detection/src/layers/hsl_cycle.rs
Normal file
55
beat_detection/src/layers/hsl_cycle.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use palette::{Hsl, IntoColor, Pixel, Srgb};
|
||||||
|
use sdl2::{pixels::Color, rect::Rect};
|
||||||
|
|
||||||
|
use crate::layers::OutputLayer;
|
||||||
|
|
||||||
|
pub struct HSLCycle {
|
||||||
|
cycle_millis: usize,
|
||||||
|
time: usize,
|
||||||
|
rgb: [u8; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HSLCycle {
|
||||||
|
pub fn new(cycle_length: Duration) -> Self {
|
||||||
|
Self {
|
||||||
|
cycle_millis: cycle_length.as_millis() as usize,
|
||||||
|
time: 0,
|
||||||
|
rgb: [0, 0, 0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputLayer for HSLCycle {
|
||||||
|
fn tick(&mut self, dt: std::time::Duration) {
|
||||||
|
self.time += dt.as_millis() as usize;
|
||||||
|
self.time %= self.cycle_millis;
|
||||||
|
|
||||||
|
let hsl: Srgb = Hsl::new(
|
||||||
|
360.0 * (self.time as f32 / self.cycle_millis as f32),
|
||||||
|
1.0,
|
||||||
|
0.5,
|
||||||
|
)
|
||||||
|
.into_color();
|
||||||
|
|
||||||
|
self.rgb = hsl.into_format().into_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_sdl(&self, canvas: &mut sdl2::render::Canvas<sdl2::video::Window>, _texture_size: u32) {
|
||||||
|
let [r, g, b] = self.rgb;
|
||||||
|
canvas.set_draw_color(Color::RGB(r, g, b));
|
||||||
|
canvas.fill_rect(Rect::new(0, 0, 5, 5)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
for head in output.iter_mut() {
|
||||||
|
head.rgbw = (r, g, b, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
beat_detection/src/layers/mod.rs
Normal file
18
beat_detection/src/layers/mod.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
pub mod hsl_cycle;
|
||||||
|
pub mod beat_detector;
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use sdl2::{event::Event, render::Canvas, video::Window};
|
||||||
|
|
||||||
|
use crate::fixtures::MovingHead;
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
@ -1,59 +1,42 @@
|
|||||||
mod capture;
|
mod capture;
|
||||||
|
mod dmx_controller;
|
||||||
mod dsp;
|
mod dsp;
|
||||||
mod fixtures;
|
mod fixtures;
|
||||||
|
mod layers;
|
||||||
mod util;
|
mod util;
|
||||||
mod dmx_controller;
|
|
||||||
|
|
||||||
use std::{sync::{Arc, Mutex}, thread};
|
use std::{sync::{Arc, Mutex}, thread, time::Duration};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use pulse::sample::{Format, Spec};
|
use layers::{OutputLayer, beat_detector::BeatDetectinator, hsl_cycle::HSLCycle};
|
||||||
use ringbuffer::{ConstGenericRingBuffer, RingBuffer, RingBufferExt, RingBufferWrite};
|
use sdl2::{event::Event, keyboard::Keycode, pixels::Color, rect::Rect};
|
||||||
use sdl2::{
|
|
||||||
event::Event,
|
|
||||||
keyboard::Keycode,
|
|
||||||
pixels::Color,
|
|
||||||
rect::{Point, Rect},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{dmx_controller::controller_thread, dsp::ZTransformFilter};
|
use crate::dmx_controller::controller_thread;
|
||||||
|
use crate::fixtures::MovingHead;
|
||||||
|
|
||||||
const SAMPLE_RATE: usize = 5000;
|
|
||||||
const FPS: usize = 50;
|
const FPS: usize = 50;
|
||||||
|
|
||||||
const BUFFER_SIZE: usize = SAMPLE_RATE / FPS;
|
|
||||||
|
|
||||||
const POINT_COUNT: usize = SAMPLE_RATE / 200;
|
|
||||||
const POINT_BUFFER_SIZE: usize = POINT_COUNT.next_power_of_two();
|
|
||||||
|
|
||||||
const MIN_MAX_SAMPLE_COUNT: usize = 10;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let spec = Spec {
|
// dmx thread
|
||||||
format: Format::F32le,
|
|
||||||
rate: SAMPLE_RATE as u32,
|
|
||||||
channels: 1,
|
|
||||||
};
|
|
||||||
assert!(spec.is_valid());
|
|
||||||
|
|
||||||
let dmx_running = Arc::new(Mutex::new(true));
|
let dmx_running = Arc::new(Mutex::new(true));
|
||||||
let brightness = Arc::new(Mutex::new(0f32));
|
let movingheads = Arc::new(Mutex::new([
|
||||||
|
MovingHead::new(1),
|
||||||
|
MovingHead::new(15),
|
||||||
|
MovingHead::new(29),
|
||||||
|
MovingHead::new(43),
|
||||||
|
]));
|
||||||
|
|
||||||
let dmx_thread = {
|
let dmx_thread = {
|
||||||
let dmx_running = dmx_running.clone();
|
let dmx_running = dmx_running.clone();
|
||||||
let brightness = brightness.clone();
|
let movingheads = movingheads.clone();
|
||||||
thread::spawn(move || controller_thread(dmx_running, brightness))
|
thread::spawn(move || controller_thread(dmx_running, movingheads))
|
||||||
};
|
};
|
||||||
|
|
||||||
let reader = capture::get_audio_reader(&spec)?;
|
// output layers
|
||||||
let mut buffer = [0u8; 4 * BUFFER_SIZE];
|
|
||||||
|
|
||||||
let mut bass_filter = dsp::BassFilter::default();
|
let mut beat_detector = BeatDetectinator::new();
|
||||||
let mut envelope_filter = dsp::EnvelopeFilter::default();
|
let mut hsl_cycle = HSLCycle::new(Duration::from_secs(5));
|
||||||
let mut beat_filter = dsp::BeatFilter::default();
|
|
||||||
let mut j = 0;
|
|
||||||
|
|
||||||
let mut threshold = 1.5f32;
|
|
||||||
|
|
||||||
// sdl
|
// sdl
|
||||||
|
|
||||||
@ -66,15 +49,14 @@ fn main() -> Result<()> {
|
|||||||
.build()?;
|
.build()?;
|
||||||
let mut canvas = window.into_canvas().build()?;
|
let mut canvas = window.into_canvas().build()?;
|
||||||
let texture_creator = canvas.texture_creator();
|
let texture_creator = canvas.texture_creator();
|
||||||
let texture_size = 10 * POINT_COUNT as u32;
|
let texture_size = 250;
|
||||||
let mut texture = texture_creator.create_texture_target(
|
let mut texture = texture_creator.create_texture_target(
|
||||||
canvas.default_pixel_format(),
|
canvas.default_pixel_format(),
|
||||||
texture_size,
|
texture_size,
|
||||||
texture_size,
|
texture_size,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut point_buf = ConstGenericRingBuffer::<f32, POINT_BUFFER_SIZE>::new();
|
let frame_duration = Duration::from_secs_f64(1.0 / FPS as f64);
|
||||||
point_buf.fill_default();
|
|
||||||
|
|
||||||
let mut event_pump = sdl.event_pump().unwrap();
|
let mut event_pump = sdl.event_pump().unwrap();
|
||||||
|
|
||||||
@ -86,176 +68,33 @@ fn main() -> Result<()> {
|
|||||||
keycode: Some(Keycode::Escape),
|
keycode: Some(Keycode::Escape),
|
||||||
..
|
..
|
||||||
} => break 'running,
|
} => break 'running,
|
||||||
Event::KeyDown {
|
|
||||||
keycode: Some(Keycode::Up),
|
event => {
|
||||||
..
|
// println!("{:?}", event);
|
||||||
} => {
|
beat_detector.on_sdl_event(&event);
|
||||||
threshold += 0.01f32;
|
hsl_cycle.on_sdl_event(&event);
|
||||||
println!("threshold: {:.2}", threshold);
|
|
||||||
}
|
}
|
||||||
Event::KeyDown {
|
|
||||||
keycode: Some(Keycode::Down),
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
threshold -= 0.01f32;
|
|
||||||
println!("threshold: {:.2}", threshold);
|
|
||||||
}
|
|
||||||
Event::KeyDown {
|
|
||||||
keycode: Some(Keycode::Space),
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let mut brightness = brightness.lock().unwrap();
|
|
||||||
*brightness = 1.0;
|
|
||||||
}
|
|
||||||
Event::KeyDown {
|
|
||||||
keycode: Some(k), ..
|
|
||||||
} => {
|
|
||||||
println!("{}", k)
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.read(&mut buffer)?;
|
beat_detector.tick(frame_duration);
|
||||||
|
hsl_cycle.tick(frame_duration);
|
||||||
for i in 0..BUFFER_SIZE {
|
|
||||||
let mut float_bytes = [0u8; 4];
|
{
|
||||||
float_bytes.copy_from_slice(&buffer[4 * i..4 * i + 4]);
|
let mut output = movingheads.lock().unwrap();
|
||||||
|
beat_detector.update_dmx(&mut *output);
|
||||||
j += 1;
|
hsl_cycle.update_dmx(&mut *output);
|
||||||
let sample = f32::from_le_bytes(float_bytes);
|
|
||||||
let mut value = bass_filter.process(sample);
|
|
||||||
|
|
||||||
if value < 0f32 {
|
|
||||||
value = -value;
|
|
||||||
}
|
|
||||||
|
|
||||||
let envelope = envelope_filter.process(value);
|
|
||||||
|
|
||||||
if j == 200 {
|
|
||||||
let beat = beat_filter.process(envelope);
|
|
||||||
point_buf.push(beat);
|
|
||||||
|
|
||||||
canvas.with_texture_canvas(&mut texture, |canvas| {
|
|
||||||
let min_y = 0f32;
|
|
||||||
let max_y = 3f32;
|
|
||||||
|
|
||||||
let get_y = |y: &f32| {
|
|
||||||
|
|
||||||
let mut y = y.clone();
|
|
||||||
|
|
||||||
if y <= 0f32 {
|
|
||||||
y = 0f32;
|
|
||||||
}
|
|
||||||
|
|
||||||
y = (1f32 + y).log2();
|
|
||||||
|
|
||||||
let y = (y - min_y) / (max_y - min_y);
|
|
||||||
|
|
||||||
((1f32 - y) * texture_size as f32) as u32
|
|
||||||
};
|
|
||||||
|
|
||||||
// background
|
|
||||||
let v;
|
|
||||||
{
|
|
||||||
let mut brightness = brightness.lock().unwrap();
|
|
||||||
*brightness = if beat > threshold {
|
|
||||||
1f32
|
|
||||||
} else {
|
|
||||||
0.75f32 * *brightness
|
|
||||||
};
|
|
||||||
|
|
||||||
v = (255f32 * *brightness) as u8;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.set_draw_color(Color::RGB(v, v, v));
|
|
||||||
canvas.clear();
|
|
||||||
|
|
||||||
// zero
|
|
||||||
|
|
||||||
canvas.set_draw_color(Color::RGB(0, 128, 0));
|
|
||||||
let y = get_y(&0f32);
|
|
||||||
|
|
||||||
canvas
|
|
||||||
.draw_line(
|
|
||||||
Point::new(0, y as i32),
|
|
||||||
Point::new(texture_size as i32, y as i32),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// min / max lines
|
|
||||||
|
|
||||||
canvas.set_draw_color(Color::RGB(255, 0, 0));
|
|
||||||
|
|
||||||
let min_beat = point_buf
|
|
||||||
.iter()
|
|
||||||
.skip(point_buf.capacity() - MIN_MAX_SAMPLE_COUNT)
|
|
||||||
.reduce(|a, b| if a < b { a } else { b })
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let x = texture_size - MIN_MAX_SAMPLE_COUNT as u32 * 10;
|
|
||||||
|
|
||||||
let y = get_y(min_beat);
|
|
||||||
|
|
||||||
canvas
|
|
||||||
.draw_line(
|
|
||||||
Point::new(x as i32, y as i32),
|
|
||||||
Point::new(texture_size as i32, y as i32),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let max_beat = point_buf
|
|
||||||
.iter()
|
|
||||||
.skip(point_buf.capacity() - MIN_MAX_SAMPLE_COUNT)
|
|
||||||
.reduce(|a, b| if a > b { a } else { b })
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let y = get_y(max_beat);
|
|
||||||
|
|
||||||
canvas
|
|
||||||
.draw_line(
|
|
||||||
Point::new(x as i32, y as i32),
|
|
||||||
Point::new(texture_size as i32, y as i32),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// threshhold line
|
|
||||||
|
|
||||||
canvas.set_draw_color(Color::RGB(0, 0, 255));
|
|
||||||
let y = get_y(&threshold);
|
|
||||||
|
|
||||||
canvas
|
|
||||||
.draw_line(
|
|
||||||
Point::new(0, y as i32),
|
|
||||||
Point::new(texture_size as i32, y as i32),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// values
|
|
||||||
|
|
||||||
canvas.set_draw_color(Color::RGB(0, 255, 0));
|
|
||||||
|
|
||||||
for (i, beat) in point_buf
|
|
||||||
.iter()
|
|
||||||
.skip(point_buf.capacity() - POINT_COUNT)
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
let x = 10 * i;
|
|
||||||
let y = get_y(beat);
|
|
||||||
|
|
||||||
canvas
|
|
||||||
.draw_rect(Rect::new((x + 1) as i32, (y - 1) as i32, 8, 3))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
j = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.set_draw_color(Color::RGB(0, 0, 0));
|
canvas.set_draw_color(Color::RGB(0, 0, 0));
|
||||||
canvas.clear();
|
canvas.clear();
|
||||||
|
|
||||||
|
canvas.with_texture_canvas(&mut texture, |canvas| {
|
||||||
|
beat_detector.draw_sdl(canvas, texture_size);
|
||||||
|
hsl_cycle.draw_sdl(canvas, texture_size);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
|
||||||
let (w, h) = canvas.window().drawable_size();
|
let (w, h) = canvas.window().drawable_size();
|
||||||
|
|
||||||
let target_rect = if w > h {
|
let target_rect = if w > h {
|
||||||
@ -266,8 +105,9 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
canvas.copy(&texture, None, Some(target_rect)).unwrap();
|
canvas.copy(&texture, None, Some(target_rect)).unwrap();
|
||||||
canvas.present();
|
canvas.present();
|
||||||
|
thread::sleep(frame_duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut dmx_running = dmx_running.lock().unwrap();
|
let mut dmx_running = dmx_running.lock().unwrap();
|
||||||
*dmx_running = false;
|
*dmx_running = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user