Implement "particles" animation in Rust
This commit is contained in:
parent
a9202d8303
commit
7929861ad4
64
Cargo.lock
generated
64
Cargo.lock
generated
|
@ -121,6 +121,17 @@ dependencies = [
|
||||||
"num-complex",
|
"num-complex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.23.0"
|
version = "0.23.0"
|
||||||
|
@ -193,6 +204,7 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"fftw",
|
"fftw",
|
||||||
"mlua",
|
"mlua",
|
||||||
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -238,6 +250,12 @@ version = "0.3.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
|
@ -256,6 +274,46 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
"rand_hc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rawpointer"
|
name = "rawpointer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -296,3 +354,9 @@ name = "unicode-xid"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
|
@ -10,3 +10,4 @@ edition = "2018"
|
||||||
byteorder = "1.4"
|
byteorder = "1.4"
|
||||||
fftw = { version = "0.6", default-features = false, features = ["system"] }
|
fftw = { version = "0.6", default-features = false, features = ["system"] }
|
||||||
mlua = { version = "0.5", features = ["lua53"] }
|
mlua = { version = "0.5", features = ["lua53"] }
|
||||||
|
rand = "0.8"
|
||||||
|
|
257
src/animation.rs
Normal file
257
src/animation.rs
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
// vim: noet
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::error::Error as StdError;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
|
use crate::signal_processing::SignalProcessing;
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, AnimationError>;
|
||||||
|
|
||||||
|
/////////// Error Type and Implementation ////////////
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AnimationError
|
||||||
|
{
|
||||||
|
LuaError(mlua::Error),
|
||||||
|
ErrorMessage(std::string::String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for AnimationError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
AnimationError::LuaError(e) => f.write_fmt(format_args!("=== Lua Error ===\n{}\n=================", e))?,
|
||||||
|
AnimationError::ErrorMessage(s) => f.write_fmt(format_args!("Message({})", s))?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdError for AnimationError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
AnimationError::LuaError(_) => "Lua Error",
|
||||||
|
AnimationError::ErrorMessage(_) => "Error Message",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////// Helper Structs ////////////
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Color
|
||||||
|
{
|
||||||
|
pub r: f32,
|
||||||
|
pub g: f32,
|
||||||
|
pub b: f32,
|
||||||
|
pub w: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color
|
||||||
|
{
|
||||||
|
pub fn scale(&mut self, factor: f32)
|
||||||
|
{
|
||||||
|
self.r *= factor;
|
||||||
|
self.g *= factor;
|
||||||
|
self.b *= factor;
|
||||||
|
self.w *= factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _limit_component(c: &mut f32)
|
||||||
|
{
|
||||||
|
if *c > 1.0 {
|
||||||
|
*c = 1.0;
|
||||||
|
} else if *c < 0.0 {
|
||||||
|
*c = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn limit(&mut self)
|
||||||
|
{
|
||||||
|
Color::_limit_component(&mut self.r);
|
||||||
|
Color::_limit_component(&mut self.g);
|
||||||
|
Color::_limit_component(&mut self.b);
|
||||||
|
Color::_limit_component(&mut self.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ref_by_index_mut(&mut self, i: usize) -> Option<&mut f32>
|
||||||
|
{
|
||||||
|
match i {
|
||||||
|
0 => Some(&mut self.r),
|
||||||
|
1 => Some(&mut self.g),
|
||||||
|
2 => Some(&mut self.b),
|
||||||
|
3 => Some(&mut self.w),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ref_by_index(&self, i: usize) -> Option<&f32>
|
||||||
|
{
|
||||||
|
match i {
|
||||||
|
0 => Some(&self.r),
|
||||||
|
1 => Some(&self.g),
|
||||||
|
2 => Some(&self.b),
|
||||||
|
3 => Some(&self.w),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////// Animation Trait ////////////
|
||||||
|
|
||||||
|
pub trait Animation {
|
||||||
|
fn new(sigproc: Rc<RefCell<SignalProcessing>>) -> Self;
|
||||||
|
|
||||||
|
fn init(&mut self) -> Result<()>;
|
||||||
|
fn periodic(&mut self) -> Result<()>;
|
||||||
|
|
||||||
|
fn get_colorlist(&self) -> &[ [Color; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS];
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////// Animation implementations ////////////
|
||||||
|
|
||||||
|
pub mod particles
|
||||||
|
{
|
||||||
|
use crate::animation::{Color, Animation, Result};
|
||||||
|
use crate::signal_processing::SignalProcessing;
|
||||||
|
use crate::config;
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
const COOLDOWN_FACTOR : f32 = 0.99995;
|
||||||
|
const RGB_EXPONENT : f32 = 1.8;
|
||||||
|
const W_EXPONENT : f32 = 2.2;
|
||||||
|
const FADE_FACTOR : f32 = 0.98;
|
||||||
|
const AVG_LEDS_ACTIVATED : f32 = 0.05;
|
||||||
|
const WHITE_EXTRA_SCALE : f32 = 0.5;
|
||||||
|
|
||||||
|
pub struct Particles
|
||||||
|
{
|
||||||
|
energy : [ [Color; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS],
|
||||||
|
max_energy : Color,
|
||||||
|
|
||||||
|
colorlists : [ [Color; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS],
|
||||||
|
|
||||||
|
sigproc: Rc<RefCell<SignalProcessing>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Animation for Particles
|
||||||
|
{
|
||||||
|
fn new(sigproc: Rc<RefCell<SignalProcessing>>) -> Particles
|
||||||
|
{
|
||||||
|
Particles {
|
||||||
|
energy: [ [Color{r: 0.0, g: 0.0, b: 0.0, w: 0.0}; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS],
|
||||||
|
max_energy: Color{r: 1.0, g: 1.0, b: 1.0, w: 1.0},
|
||||||
|
colorlists: [ [Color{r: 0.0, g: 0.0, b: 0.0, w: 0.0}; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS],
|
||||||
|
sigproc: sigproc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self) -> Result<()>
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn periodic(&mut self) -> Result<()>
|
||||||
|
{
|
||||||
|
let sigproc = self.sigproc.borrow();
|
||||||
|
|
||||||
|
// extract frequency band energies
|
||||||
|
let cur_energy = Color{
|
||||||
|
r: sigproc.get_energy_in_band( 0.0, 400.0),
|
||||||
|
g: sigproc.get_energy_in_band( 400.0, 4000.0),
|
||||||
|
b: sigproc.get_energy_in_band( 4000.0, 12000.0),
|
||||||
|
w: sigproc.get_energy_in_band(12000.0, 22000.0)};
|
||||||
|
|
||||||
|
// track the maximum energy with cooldown
|
||||||
|
self.max_energy.r *= COOLDOWN_FACTOR;
|
||||||
|
if cur_energy.r > self.max_energy.r {
|
||||||
|
self.max_energy.r = cur_energy.r;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.max_energy.g *= COOLDOWN_FACTOR;
|
||||||
|
if cur_energy.g > self.max_energy.g {
|
||||||
|
self.max_energy.g = cur_energy.g;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.max_energy.b *= COOLDOWN_FACTOR;
|
||||||
|
if cur_energy.b > self.max_energy.b {
|
||||||
|
self.max_energy.b = cur_energy.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.max_energy.w *= COOLDOWN_FACTOR;
|
||||||
|
if cur_energy.w > self.max_energy.w {
|
||||||
|
self.max_energy.w = cur_energy.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fade all LEDs towards black
|
||||||
|
for strip in 0..config::NUM_STRIPS {
|
||||||
|
for led in 0..config::NUM_LEDS_PER_STRIP {
|
||||||
|
self.energy[strip][led].scale(FADE_FACTOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// distribute the energy for each color
|
||||||
|
let new_energy = Color{
|
||||||
|
r: (cur_energy.r / self.max_energy.r).powf(RGB_EXPONENT),
|
||||||
|
g: (cur_energy.g / self.max_energy.g).powf(RGB_EXPONENT),
|
||||||
|
b: (cur_energy.b / self.max_energy.b).powf(RGB_EXPONENT),
|
||||||
|
w: (cur_energy.w / self.max_energy.w).powf(W_EXPONENT),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut remaining_energy = new_energy;
|
||||||
|
remaining_energy.scale(AVG_LEDS_ACTIVATED * config::NUM_LEDS_TOTAL as f32);
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
|
// FIXME: how to call this code for green, blue and white as well without too much
|
||||||
|
// duplication?
|
||||||
|
for coloridx in 0..=3 {
|
||||||
|
let new_energy_ref = new_energy.ref_by_index(coloridx).unwrap();
|
||||||
|
let rem_energy_ref = remaining_energy.ref_by_index_mut(coloridx).unwrap();
|
||||||
|
|
||||||
|
while *rem_energy_ref > 0.0 {
|
||||||
|
let mut rnd_energy = rng.gen::<f32>() * (*new_energy_ref) * 5.0;
|
||||||
|
|
||||||
|
let rnd_strip = rng.gen_range(0..config::NUM_STRIPS);
|
||||||
|
let rnd_led = rng.gen_range(0..config::NUM_LEDS_PER_STRIP);
|
||||||
|
|
||||||
|
if rnd_energy > *rem_energy_ref {
|
||||||
|
rnd_energy = *rem_energy_ref;
|
||||||
|
*rem_energy_ref = 0.0;
|
||||||
|
} else {
|
||||||
|
*rem_energy_ref -= rnd_energy;
|
||||||
|
}
|
||||||
|
|
||||||
|
let led_ref = self.energy[rnd_strip][rnd_led].ref_by_index_mut(coloridx).unwrap();
|
||||||
|
*led_ref += rnd_energy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// color post-processing
|
||||||
|
self.colorlists = self.energy;
|
||||||
|
|
||||||
|
for strip in 0..config::NUM_STRIPS {
|
||||||
|
for led in 0..config::NUM_LEDS_PER_STRIP {
|
||||||
|
self.colorlists[strip][led].w *= WHITE_EXTRA_SCALE;
|
||||||
|
|
||||||
|
self.colorlists[strip][led].limit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_colorlist(&self) -> &[ [Color; config::NUM_LEDS_PER_STRIP]; config::NUM_STRIPS]
|
||||||
|
{
|
||||||
|
return &self.colorlists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
src/main.rs
39
src/main.rs
|
@ -9,10 +9,12 @@ mod signal_processing;
|
||||||
mod config;
|
mod config;
|
||||||
mod userscript;
|
mod userscript;
|
||||||
mod udpproto;
|
mod udpproto;
|
||||||
|
mod animation;
|
||||||
|
|
||||||
use crate::signal_processing::SignalProcessing;
|
use crate::signal_processing::SignalProcessing;
|
||||||
use crate::userscript::UserScript;
|
//use crate::userscript::UserScript;
|
||||||
use crate::udpproto::UdpProto;
|
use crate::udpproto::UdpProto;
|
||||||
|
use crate::animation::Animation;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -40,6 +42,7 @@ fn main()
|
||||||
let sigproc = Rc::new(RefCell::new(
|
let sigproc = Rc::new(RefCell::new(
|
||||||
SignalProcessing::new(config::BLOCK_LEN, config::SAMP_RATE).unwrap()));
|
SignalProcessing::new(config::BLOCK_LEN, config::SAMP_RATE).unwrap()));
|
||||||
|
|
||||||
|
/*
|
||||||
// set up Lua environment
|
// set up Lua environment
|
||||||
|
|
||||||
println!("Loading user script...");
|
println!("Loading user script...");
|
||||||
|
@ -61,6 +64,15 @@ fn main()
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
println!("Contructing Animation...");
|
||||||
|
|
||||||
|
let mut anim: animation::particles::Particles = animation::Animation::new(sigproc.clone());
|
||||||
|
|
||||||
|
println!("Calling Animation::init()...");
|
||||||
|
|
||||||
|
anim.init().unwrap();
|
||||||
|
|
||||||
println!("Done! Starting main loop…");
|
println!("Done! Starting main loop…");
|
||||||
|
|
||||||
|
@ -112,24 +124,27 @@ fn main()
|
||||||
}
|
}
|
||||||
|
|
||||||
// call the periodic function in the user script
|
// call the periodic function in the user script
|
||||||
match script.periodic() {
|
match anim.periodic() {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("=== Lua Error ===\n{}\n====> Terminating.", e);
|
println!("=== Animation Error ===\n{}\n====> Terminating.", e);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: only send with 60 FPS!
|
|
||||||
|
|
||||||
if Instant::now() > next_send_instant {
|
if Instant::now() > next_send_instant {
|
||||||
for i in 0..script.colorlists[0].len() {
|
let colorlists = anim.get_colorlist();
|
||||||
udpproto.set_color((i / config::NUM_LEDS_PER_STRIP) as u8,
|
|
||||||
(i % config::NUM_LEDS_PER_STRIP) as u8,
|
for i in 0..config::NUM_LEDS_TOTAL {
|
||||||
(script.colorlists[0][i] * 255.0) as u8,
|
let strip = i / config::NUM_LEDS_PER_STRIP;
|
||||||
(script.colorlists[1][i] * 255.0) as u8,
|
let led = i % config::NUM_LEDS_PER_STRIP;
|
||||||
(script.colorlists[2][i] * 255.0) as u8,
|
|
||||||
(script.colorlists[3][i] * 255.0) as u8).unwrap();
|
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).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
udpproto.commit().unwrap();
|
udpproto.commit().unwrap();
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
// vim: noet
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Module for the Lua scripting interface of Musiclight.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Test code for reference
|
|
||||||
// test the mlua crate
|
|
||||||
let lua_state = Lua::new();
|
|
||||||
|
|
||||||
lua_state.globals().set("get_rust_value", lua_state.create_function(|_, ()| {
|
|
||||||
Ok(3)
|
|
||||||
}).unwrap()).unwrap();
|
|
||||||
|
|
||||||
let user_script = std::fs::read_to_string("test.lua").unwrap();
|
|
||||||
lua_state.load(&user_script).exec().unwrap();
|
|
||||||
|
|
||||||
let lua_func_test : mlua::Function = lua_state.globals().get("test").unwrap();
|
|
||||||
|
|
||||||
println!("{}", lua_func_test.call::<_, u32>(123).unwrap());
|
|
||||||
*/
|
|
||||||
|
|
||||||
use crate::config;
|
|
||||||
use crate::signal_processing::SignalProcessing;
|
|
||||||
|
|
||||||
use mlua::Lua;
|
|
||||||
use mlua::FromLua;
|
|
||||||
use mlua::Error;
|
|
||||||
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
pub struct UserScript
|
|
||||||
{
|
|
||||||
lua_state: Lua,
|
|
||||||
|
|
||||||
pub colorlists: [ [f32; config::NUM_LEDS_TOTAL]; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserScript
|
|
||||||
{
|
|
||||||
pub fn new(sigproc: Rc<RefCell<SignalProcessing>>, user_script_path: &str) -> std::result::Result<UserScript, mlua::Error>
|
|
||||||
{
|
|
||||||
let s = UserScript {
|
|
||||||
lua_state: Lua::new(),
|
|
||||||
colorlists: [ [0f32; config::NUM_LEDS_TOTAL]; 4],
|
|
||||||
};
|
|
||||||
|
|
||||||
// provide some configuration constants to Lua via a table
|
|
||||||
let config_table = s.lua_state.create_table()?;
|
|
||||||
|
|
||||||
config_table.set("sampling_rate", config::SAMP_RATE)?;
|
|
||||||
config_table.set("block_length", config::BLOCK_LEN)?;
|
|
||||||
config_table.set("samples_per_update", config::SAMPLES_PER_UPDATE)?;
|
|
||||||
|
|
||||||
s.lua_state.globals().set("CONFIG", config_table)?;
|
|
||||||
|
|
||||||
// register the signal processing reference as Lua user data
|
|
||||||
s.lua_state.globals().set("sigproc", SignalProcessingWrapper{
|
|
||||||
signal_processing: sigproc
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// register the LED interface
|
|
||||||
s.lua_state.globals().set("leds", LuaLED{})?;
|
|
||||||
|
|
||||||
// load the user script and execute it to make variables and functions available
|
|
||||||
let user_script = std::fs::read_to_string(user_script_path)?;
|
|
||||||
s.lua_state.load(&user_script).exec()?;
|
|
||||||
|
|
||||||
Ok(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(&self) -> std::result::Result<(), mlua::Error>
|
|
||||||
{
|
|
||||||
// find the init functions
|
|
||||||
let lua_init_func: mlua::Function = self.lua_state.globals().get("init")?;
|
|
||||||
|
|
||||||
lua_init_func.call( (config::NUM_STRIPS, config::NUM_LEDS_PER_STRIP) )?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn periodic(&mut self) -> std::result::Result<(), mlua::Error>
|
|
||||||
{
|
|
||||||
// find the init functions
|
|
||||||
let lua_periodic_func: mlua::Function = self.lua_state.globals().get("periodic")?;
|
|
||||||
|
|
||||||
// call the script's periodic() function, which (hopefully) returns four Tables with color
|
|
||||||
// values
|
|
||||||
let rvals = lua_periodic_func.call::<_, mlua::MultiValue>( () )?;
|
|
||||||
|
|
||||||
// check the number of returned values
|
|
||||||
if rvals.len() != 4 {
|
|
||||||
return Err(Error::RuntimeError("Wrong number of return values from 'periodic'. Expected 4.".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert the Lua Tables to normal vectors
|
|
||||||
let mut i = 0;
|
|
||||||
for rval in rvals {
|
|
||||||
let table = mlua::Table::from_lua(rval, &self.lua_state)?;
|
|
||||||
|
|
||||||
let v = table.sequence_values()
|
|
||||||
.map(|x| x.unwrap())
|
|
||||||
.collect::<Vec<f32>>();
|
|
||||||
|
|
||||||
// check the length of the color array
|
|
||||||
if v.len() != config::NUM_LEDS_TOTAL {
|
|
||||||
return Err(Error::RuntimeError("Number of color values returned from 'periodic' must match number of LEDs given in 'init'.".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
for j in 0 .. self.colorlists[i].len() {
|
|
||||||
self.colorlists[i][j] = v[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Wrap a SignalProcessing instance and provide a Lua interface for some of its methods.
|
|
||||||
*/
|
|
||||||
struct SignalProcessingWrapper
|
|
||||||
{
|
|
||||||
signal_processing: Rc<RefCell<SignalProcessing>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl mlua::UserData for SignalProcessingWrapper
|
|
||||||
{
|
|
||||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M)
|
|
||||||
{
|
|
||||||
methods.add_method("get_energy_in_band", |_lua, this, (start_freq, end_freq): (f32, f32)| {
|
|
||||||
Ok(this.signal_processing.borrow().get_energy_in_band(start_freq, end_freq))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Lua interface for functions related to the LED setup
|
|
||||||
*/
|
|
||||||
struct LuaLED
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl mlua::UserData for LuaLED
|
|
||||||
{
|
|
||||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M)
|
|
||||||
{
|
|
||||||
methods.add_method("idx", |_lua, _this, (strip, led): (usize, usize)| {
|
|
||||||
Ok( (strip - 1) * config::NUM_LEDS_PER_STRIP + (led - 1) + 1 )
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
31
test.lua
31
test.lua
|
@ -1,31 +0,0 @@
|
||||||
function init(nstrip, nmod)
|
|
||||||
print("Initializing with "..nstrip.." strips with "..nmod.." modules each.")
|
|
||||||
|
|
||||||
print("Sampling rate: "..CONFIG['sampling_rate'].." Hz")
|
|
||||||
|
|
||||||
local nled = nstrip * nmod
|
|
||||||
|
|
||||||
red = {}
|
|
||||||
green = {}
|
|
||||||
blue = {}
|
|
||||||
white = {}
|
|
||||||
|
|
||||||
|
|
||||||
for i = 1,nled do
|
|
||||||
red[i] = 0.8
|
|
||||||
green[i] = 0.1
|
|
||||||
blue[i] = 0.2
|
|
||||||
white[i] = 0.1
|
|
||||||
end
|
|
||||||
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function periodic()
|
|
||||||
bass = sigproc:get_energy_in_band(0, 400)
|
|
||||||
mid = sigproc:get_energy_in_band(400, 4000)
|
|
||||||
treble = sigproc:get_energy_in_band(4000, 20000)
|
|
||||||
|
|
||||||
print("Bass: "..bass.." – Mid: "..mid.." – Treble: "..treble)
|
|
||||||
return red, green, blue, white
|
|
||||||
end
|
|
Loading…
Reference in a new issue