From bd18905a9640d1ff49587749856a362e77cd87b0 Mon Sep 17 00:00:00 2001 From: Patrick Moessler <pub@asaril.de> Date: Wed, 19 Mar 2025 03:42:29 +0100 Subject: [PATCH] restructure into modules --- src/audio.rs | 111 ++++++++++++ src/config.rs | 5 + src/effects/bass_sparks.rs | 42 +++++ src/effects/led_effect.rs | 123 +++++++++++++ src/effects/mod.rs | 2 + src/helpers.rs | 33 ++++ src/main.rs | 359 ++++--------------------------------- 7 files changed, 350 insertions(+), 325 deletions(-) create mode 100644 src/audio.rs create mode 100644 src/config.rs create mode 100644 src/effects/bass_sparks.rs create mode 100644 src/effects/led_effect.rs create mode 100644 src/effects/mod.rs create mode 100644 src/helpers.rs diff --git a/src/audio.rs b/src/audio.rs new file mode 100644 index 0000000..7cbdf72 --- /dev/null +++ b/src/audio.rs @@ -0,0 +1,111 @@ +use esp_idf_svc::sys::{esp_dsp, esp_nofail}; + +use crate::config::{AUDIO_BANDS, AUDIO_BUFFERS, AUDIO_SAMPLES_PER_BUF}; +use crate::helpers::{falloff, falloff_f}; + +pub type AudioBuffer = [i32; AUDIO_SAMPLES_PER_BUF]; +pub type DspBuffer = [f32; AUDIO_SAMPLES_PER_BUF]; + +pub struct AudioProcessor { + pub floating_max: i32, + pub current_powers: [f32; AUDIO_BANDS], + pub avg_powers: [f32; AUDIO_BANDS], + pub fft_buffer: [DspBuffer; AUDIO_BUFFERS], + pub next_fft_buf: usize, + fft_window: DspBuffer, +} + +impl AudioProcessor { + pub fn new() -> Self { + let mut buf: DspBuffer = [0f32; AUDIO_SAMPLES_PER_BUF]; + + unsafe { + esp_dsp::dsps_wind_hann_f32(buf.as_mut_ptr(), AUDIO_SAMPLES_PER_BUF as i32); + } + + AudioProcessor { + floating_max: 0i32, + current_powers: [0f32; AUDIO_BANDS], + avg_powers: [0f32; AUDIO_BANDS], + fft_buffer: [[0f32; AUDIO_SAMPLES_PER_BUF]; AUDIO_BUFFERS], + next_fft_buf: 0, + fft_window: buf, + } + } + + pub fn process(&mut self, audio: &AudioBuffer) -> usize { + let &(mut proc_fft_buffer) = &self.fft_buffer[self.next_fft_buf]; + + /* calculate floating max */ + let mut new_max = 0i32; + for value in audio { + new_max = std::cmp::max(new_max, value.saturating_abs()); + } + + /* get maximum */ + self.floating_max = std::cmp::max( + 10000000, + if new_max > self.floating_max { + new_max + } else { + falloff(self.floating_max, falloff(self.floating_max, new_max)) + }, + ); + + /* convert to floats for input to fft */ + for it in audio.iter().zip(proc_fft_buffer.iter_mut()) { + let (audio_it, fft_it) = it; + *fft_it = (*audio_it as f32) / (i32::MAX as f32); + } + + /* do fft */ + let half_sample_count = (AUDIO_SAMPLES_PER_BUF / 2) as i32; + unsafe { + esp_nofail!(esp_dsp::dsps_mul_f32_ae32( + proc_fft_buffer.as_ptr(), + self.fft_window.as_ptr(), + proc_fft_buffer.as_mut_ptr(), + AUDIO_SAMPLES_PER_BUF as i32, + 1, + 1, + 1, + )); + + esp_nofail!(esp_dsp::dsps_fft2r_fc32_aes3_( + proc_fft_buffer.as_mut_ptr(), + half_sample_count, + esp_dsp::dsps_fft_w_table_fc32, + )); // operating on half length but complex + esp_nofail!(esp_dsp::dsps_bit_rev2r_fc32( + proc_fft_buffer.as_mut_ptr(), + half_sample_count + )); // operating on half length but complex + esp_nofail!(esp_dsp::dsps_cplx2real_fc32_ae32_( + proc_fft_buffer.as_mut_ptr(), + half_sample_count, + esp_dsp::dsps_fft_w_table_fc32, + esp_dsp::dsps_fft_w_table_size, + )); // operating on half length but complex + + for i in 0..half_sample_count as usize { + proc_fft_buffer[i] = (proc_fft_buffer[i * 2] * proc_fft_buffer[i * 2] + + proc_fft_buffer[i * 2 + 1] * proc_fft_buffer[i * 2 + 1]) + .sqrt(); + } + } + + /* do band stats */ + self.current_powers[0] = proc_fft_buffer[1..8].iter().sum::<f32>() / 8f32; + self.current_powers[1] = proc_fft_buffer[9..86].iter().sum::<f32>() / 78f32; + self.current_powers[2] = proc_fft_buffer[87..470].iter().sum::<f32>() / 384f32; + + for it in self.current_powers.iter().zip(self.avg_powers.iter_mut()) { + let (current, avg) = it; + *avg = falloff_f(*avg, *current); + } + + let last_fft_buf = self.next_fft_buf; + self.next_fft_buf = (self.next_fft_buf + 1) % AUDIO_BUFFERS; + last_fft_buf + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..5ac7b5b --- /dev/null +++ b/src/config.rs @@ -0,0 +1,5 @@ +pub const LED_COUNT: usize = 72; + +pub const AUDIO_SAMPLES_PER_BUF: usize = 1024; +pub const AUDIO_BUFFERS: usize = 2; +pub const AUDIO_BANDS: usize = 3; diff --git a/src/effects/bass_sparks.rs b/src/effects/bass_sparks.rs new file mode 100644 index 0000000..f9b361a --- /dev/null +++ b/src/effects/bass_sparks.rs @@ -0,0 +1,42 @@ +use esp_idf_svc::hal::units::{MilliSeconds,FromValueType}; + +use crate::audio::AudioProcessor; +use crate::effects::led_effect::{LedColors, LedEffect, Rgbv}; +use crate::helpers::random_at_most; +use crate::LED_COUNT; + +pub struct LedEffectBassSparks { + bass_color: Rgbv, +} +impl LedEffectBassSparks { + pub fn new() -> Self { + Self { + bass_color: Rgbv::black(0), + } + } +} +impl LedEffect for LedEffectBassSparks { + fn render(&mut self, processed: &AudioProcessor, _fft_buf: usize, leds: &mut LedColors) -> MilliSeconds { + if processed.floating_max > 10100000 + && (processed.current_powers[0] > 1.25 * processed.avg_powers[0]) + { + self.bass_color = Rgbv::new(127, 0, 255, 4) + } + + leds.fill(self.bass_color); + + self.bass_color.decrease(3, 5, 5, 0); + + if processed.floating_max > 10100000 + && (processed.current_powers[1] > 1.35 * processed.avg_powers[1]) + && (processed.current_powers[2] > 1.35 * processed.avg_powers[2]) + { + for _ in 0..10 { + let led_index = random_at_most(LED_COUNT as u32 - 1) as usize; + leds[led_index] = Rgbv::white(31); + } + } + + 10.ms() + } +} diff --git a/src/effects/led_effect.rs b/src/effects/led_effect.rs new file mode 100644 index 0000000..837c6b1 --- /dev/null +++ b/src/effects/led_effect.rs @@ -0,0 +1,123 @@ +use anyhow::{bail, Result}; +use bytemuck::{Pod, Zeroable}; +use esp_idf_svc::hal::units::MilliSeconds; + +use crate::{config::LED_COUNT, AudioProcessor}; + +pub trait LedEffect { + fn render(&mut self, processed: &AudioProcessor, fft_buf: usize, leds: &mut LedColors) -> MilliSeconds; +} + +#[derive(Clone, Copy, Eq, PartialEq, Pod, Zeroable)] +#[repr(C, align(4))] +pub struct Rgbv { + _o: u8, + b: u8, + g: u8, + r: u8, +} + +impl Rgbv { + const _O_ONES: u8 = 0xE0; + + #[rustfmt::skip] pub const fn black(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0x00, g: 0x00, b: 0x00, _o: Self::_O_ONES | o } } + #[rustfmt::skip] pub const fn white(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0xFF, g: 0xFF, b: 0xFF, _o: Self::_O_ONES | o } } + + #[rustfmt::skip] pub const fn red(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0xFF, g: 0x00, b: 0x00, _o: Self::_O_ONES | o } } + #[rustfmt::skip] pub const fn green(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0x00, g: 0xFF, b: 0x00, _o: Self::_O_ONES | o } } + #[rustfmt::skip] pub const fn blue(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0x00, g: 0x00, b: 0xFF, _o: Self::_O_ONES | o } } + + #[rustfmt::skip] pub const fn cyan(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0x00, g: 0xFF, b: 0xFF, _o: Self::_O_ONES | o } } + #[rustfmt::skip] pub const fn orange(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0xFF, g: 0x80, b: 0x00, _o: Self::_O_ONES | o } } + #[rustfmt::skip] pub const fn yellow(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0xFF, g: 0xFF, b: 0x00, _o: Self::_O_ONES | o } } + #[rustfmt::skip] pub const fn pink(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0xFF, g: 0x00, b: 0xFF, _o: Self::_O_ONES | o } } + + pub const MAX_O: u8 = 31; + + pub fn new(r: u8, g: u8, b: u8, o: u8) -> Self { + assert!(o <= Self::MAX_O); + Self { + r, + g, + b, + _o: o | Self::_O_ONES, + } + } + + pub fn o(self) -> u8 { + self._o & !Self::_O_ONES + } + + #[inline(always)] + pub fn set_o(mut self, o: u8) -> Self { + assert!(o <= Self::MAX_O); + self._o = o | Self::_O_ONES; + self + } + + #[inline(always)] + pub fn increase(&mut self, r: u8, g: u8, b: u8, o: u8) -> Self { + self.r = self.r.saturating_add(r); + self.g = self.g.saturating_add(g); + self.b = self.b.saturating_add(b); + self.set_o(std::cmp::min(self.o() + o, Self::MAX_O)); + *self + } + + #[inline(always)] + pub fn decrease(&mut self, r: u8, g: u8, b: u8, o: u8) -> Self { + self.r = self.r.saturating_sub(r); + self.g = self.g.saturating_sub(g); + self.b = self.b.saturating_sub(b); + self.set_o(self.o().saturating_sub(o)); + *self + } + + /// Converts hue, saturation, value to RGB + /// // copied from rmt_neopixel example + pub fn from_hsv(h: u32, s: u32, v: u32, o: u8) -> Result<Self> { + assert!(o <= Self::MAX_O); + if h > 360 || s > 100 || v > 100 { + bail!("The given HSV values are not in valid range"); + } + let s = s as f64 / 100.0; + let v = v as f64 / 100.0; + let c = s * v; + let x = c * (1.0 - (((h as f64 / 60.0) % 2.0) - 1.0).abs()); + let m = v - c; + let (r, g, b) = match h { + 0..=59 => (c, x, 0.0), + 60..=119 => (x, c, 0.0), + 120..=179 => (0.0, c, x), + 180..=239 => (0.0, x, c), + 240..=299 => (x, 0.0, c), + _ => (c, 0.0, x), + }; + Ok(Self { + r: ((r + m) * 255.0) as u8, + g: ((g + m) * 255.0) as u8, + b: ((b + m) * 255.0) as u8, + _o: o | Self::_O_ONES, + }) + } +} + +pub type LedColors = [Rgbv; LED_COUNT]; + +#[repr(C, align(4))] +#[derive(Clone, Copy, Eq, PartialEq, Pod, Zeroable)] +pub struct LedData { + zeros: u32, + pub leds: LedColors, + ones: u32, +} + +impl LedData { + pub fn new() -> Self { + Self { + zeros: 0, + leds: [Rgbv::new(0, 0, 0, 0); LED_COUNT], + ones: 0, // sic. works as well, and triggers PWM change at the end of frame transfer instead of next one. + } + } +} diff --git a/src/effects/mod.rs b/src/effects/mod.rs new file mode 100644 index 0000000..3faa122 --- /dev/null +++ b/src/effects/mod.rs @@ -0,0 +1,2 @@ +pub mod led_effect; +pub mod bass_sparks; \ No newline at end of file diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 0000000..a1be934 --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,33 @@ +use esp_idf_svc::hal::sys::esp_random; + +pub fn falloff(old: i32, new: i32) -> i32 { + (old >> 1) + (old >> 2) + (new >> 2) +} +pub fn falloff_f(old: f32, new: f32) -> f32 { + (old / 2.0f32) + (old / 4.0f32) + (new / 4.0f32) +} + +pub fn random_at_most(max: u32) -> u32 { + // impl from https://stackoverflow.com/a/6852396, adapted to uint32/2 + // Assumes 0 <= max <= INT32_MAX + // Returns in the closed interval [0, max] + assert!(max < u32::MAX); + + let num_bins = max + 1; + let num_rand = i32::MAX as u32 + 1; + let bin_size = num_rand / num_bins; + let defect = num_rand % num_bins; + + let mut x: u32; + + loop { + unsafe { + x = esp_random() >> 1; // This is carefully written not to overflow + if num_rand - defect > x { + break; + } + } + } + // Truncated division is intentional + x / bin_size +} diff --git a/src/main.rs b/src/main.rs index 1105ca8..636d369 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,303 +1,27 @@ -use bytemuck::{bytes_of, bytes_of_mut, Pod, Zeroable}; +pub mod audio; +pub mod config; +pub mod effects; +pub mod helpers; + +use audio::{AudioBuffer, AudioProcessor}; +use bytemuck::{bytes_of, bytes_of_mut}; use esp_idf_svc::{ hal::{ - delay::FreeRtos, gpio::AnyIOPin, i2s, peripherals::Peripherals, spi, units::FromValueType, + delay::FreeRtos, + gpio::AnyIOPin, + i2s, + peripherals::Peripherals, + spi, + units::{FromValueType, MilliSeconds}, }, - sys::{esp_dsp, esp_nofail, esp_random, TickType_t}, + sys::{esp_dsp, esp_nofail, TickType_t}, }; -use anyhow::{bail, Result}; - -const LED_COUNT: usize = 72; - -const AUDIO_SAMPLES_PER_BUF: usize = 1024; -const AUDIO_BUFFERS: usize = 2; -const AUDIO_BANDS: usize = 3; - -type AudioBuffer = [i32; AUDIO_SAMPLES_PER_BUF]; -type DspBuffer = [f32; AUDIO_SAMPLES_PER_BUF]; - -#[derive(Clone, Copy, Eq, PartialEq, Pod, Zeroable)] -#[repr(C, align(4))] -struct Rgbv { - r: u8, - g: u8, - b: u8, - _o: u8, -} - -impl Rgbv { - const _O_ONES: u8 = 0xE0; - - #[rustfmt::skip] const fn black(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0x00, g: 0x00, b: 0x00, _o: Self::_O_ONES | o } } - #[rustfmt::skip] const fn white(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0xFF, g: 0xFF, b: 0xFF, _o: Self::_O_ONES | o } } - - #[rustfmt::skip] const fn red(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0xFF, g: 0x00, b: 0x00, _o: Self::_O_ONES | o } } - #[rustfmt::skip] const fn green(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0x00, g: 0xFF, b: 0x00, _o: Self::_O_ONES | o } } - #[rustfmt::skip] const fn blue(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0x00, g: 0x00, b: 0xFF, _o: Self::_O_ONES | o } } - - #[rustfmt::skip] const fn cyan(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0x00, g: 0xFF, b: 0xFF, _o: Self::_O_ONES | o } } - #[rustfmt::skip] const fn orange(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0xFF, g: 0x80, b: 0x00, _o: Self::_O_ONES | o } } - #[rustfmt::skip] const fn yellow(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0xFF, g: 0xFF, b: 0x00, _o: Self::_O_ONES | o } } - #[rustfmt::skip] const fn pink(o: u8) -> Self { assert!(o<=Self::MAX_O); Self {r: 0xFF, g: 0x00, b: 0xFF, _o: Self::_O_ONES | o } } - - const MAX_O: u8 = 31; - - pub fn new(r: u8, g: u8, b: u8, o: u8) -> Self { - assert!(o <= Self::MAX_O); - Self { - r, - g, - b, - _o: o | Self::_O_ONES, - } - } - - pub fn o(self) -> u8 { - self._o & !Self::_O_ONES - } - - #[inline(always)] - pub fn set_o(mut self, o: u8) -> Self { - assert!(o <= Self::MAX_O); - self._o = o | Self::_O_ONES; - self - } - - #[inline(always)] - pub fn increase(mut self, r: u8, g: u8, b: u8, o: u8) -> Self { - self.r = self.r.saturating_add(r); - self.g = self.g.saturating_add(g); - self.b = self.b.saturating_add(b); - self.set_o(std::cmp::min(self.o() + o, Self::MAX_O)); - self - } - - #[inline(always)] - pub fn decrease(mut self, r: u8, g: u8, b: u8, o: u8) -> Self { - self.r = self.r.saturating_sub(r); - self.g = self.g.saturating_sub(g); - self.b = self.b.saturating_sub(b); - self.set_o(self.o().saturating_sub(o)); - self - } - - /// Converts hue, saturation, value to RGB - /// // copied from rmt_neopixel example - pub fn from_hsv(h: u32, s: u32, v: u32, o: u8) -> Result<Self> { - assert!(o <= Self::MAX_O); - if h > 360 || s > 100 || v > 100 { - bail!("The given HSV values are not in valid range"); - } - let s = s as f64 / 100.0; - let v = v as f64 / 100.0; - let c = s * v; - let x = c * (1.0 - (((h as f64 / 60.0) % 2.0) - 1.0).abs()); - let m = v - c; - let (r, g, b) = match h { - 0..=59 => (c, x, 0.0), - 60..=119 => (x, c, 0.0), - 120..=179 => (0.0, c, x), - 180..=239 => (0.0, x, c), - 240..=299 => (x, 0.0, c), - _ => (c, 0.0, x), - }; - Ok(Self { - r: ((r + m) * 255.0) as u8, - g: ((g + m) * 255.0) as u8, - b: ((b + m) * 255.0) as u8, - _o: o | Self::_O_ONES, - }) - } -} - -type LedColors = [Rgbv; LED_COUNT]; - -#[repr(C, align(4))] -#[derive(Clone, Copy, Eq, PartialEq, Pod, Zeroable)] -struct LedData { - zeros: u32, - leds: LedColors, - ones: u32, -} - -impl LedData { - pub fn new() -> Self { - Self { - zeros: 0, - leds: [Rgbv::new(0, 0, 0, 0); LED_COUNT], - ones: 0, // sic. works as well, and triggers PWM change at the end of frame transfer instead of next one. - } - } -} - -fn falloff(old: i32, new: i32) -> i32 { - (old >> 1) + (old >> 2) + (new >> 2) -} -fn falloff_f(old: f32, new: f32) -> f32 { - (old / 2.0f32) + (old / 4.0f32) + (new / 4.0f32) -} - -fn random_at_most(max: u32) -> u32 { - // impl from https://stackoverflow.com/a/6852396, adapted to uint32/2 - // Assumes 0 <= max <= INT32_MAX - // Returns in the closed interval [0, max] - assert!(max < u32::MAX); - - let num_bins = max + 1; - let num_rand = i32::MAX as u32 + 1; - let bin_size = num_rand / num_bins; - let defect = num_rand % num_bins; - - let mut x: u32; - - loop { - unsafe { - x = esp_random() >> 1; // This is carefully written not to overflow - if num_rand - defect > x { - break; - } - } - } - // Truncated division is intentional - x / bin_size -} - -struct AudioProcessor { - floating_max: i32, - current_powers: [f32; AUDIO_BANDS], - avg_powers: [f32; AUDIO_BANDS], - fft_buffer: [DspBuffer; AUDIO_BUFFERS], - next_fft_buf: usize, - fft_window: DspBuffer, -} - -impl AudioProcessor { - pub fn new() -> Self { - let mut buf: DspBuffer = [0f32; AUDIO_SAMPLES_PER_BUF]; - - unsafe { - esp_dsp::dsps_wind_hann_f32(buf.as_mut_ptr(), AUDIO_SAMPLES_PER_BUF as i32); - } - - AudioProcessor { - floating_max: 0i32, - current_powers: [0f32; AUDIO_BANDS], - avg_powers: [0f32; AUDIO_BANDS], - fft_buffer: [[0f32; AUDIO_SAMPLES_PER_BUF]; AUDIO_BUFFERS], - next_fft_buf: 0, - fft_window: buf, - } - } - - pub fn process(&mut self, audio: &AudioBuffer) -> usize { - let &(mut proc_fft_buffer) = &self.fft_buffer[self.next_fft_buf]; - - /* calculate floating max */ - let mut new_max = 0i32; - for value in audio { - new_max = std::cmp::max(new_max, value.saturating_abs()); - } - - /* get maximum */ - self.floating_max = std::cmp::max( - 10000000, - if new_max > self.floating_max { - new_max - } else { - falloff(self.floating_max, falloff(self.floating_max, new_max)) - }, - ); - - /* convert to floats for input to fft */ - for it in audio.iter().zip(proc_fft_buffer.iter_mut()) { - let (audio_it, fft_it) = it; - *fft_it = (*audio_it as f32) / (i32::MAX as f32); - } - - /* do fft */ - let half_sample_count = (AUDIO_SAMPLES_PER_BUF / 2) as i32; - unsafe { - esp_nofail!(esp_dsp::dsps_mul_f32_ae32( - proc_fft_buffer.as_ptr(), - self.fft_window.as_ptr(), - proc_fft_buffer.as_mut_ptr(), - AUDIO_SAMPLES_PER_BUF as i32, - 1, - 1, - 1, - )); - - esp_nofail!(esp_dsp::dsps_fft2r_fc32_aes3_( - proc_fft_buffer.as_mut_ptr(), - half_sample_count, - esp_dsp::dsps_fft_w_table_fc32, - )); // operating on half length but complex - esp_nofail!(esp_dsp::dsps_bit_rev2r_fc32( - proc_fft_buffer.as_mut_ptr(), - half_sample_count - )); // operating on half length but complex - esp_nofail!(esp_dsp::dsps_cplx2real_fc32_ae32_( - proc_fft_buffer.as_mut_ptr(), - half_sample_count, - esp_dsp::dsps_fft_w_table_fc32, - esp_dsp::dsps_fft_w_table_size, - )); // operating on half length but complex - - for i in 0..half_sample_count as usize { - proc_fft_buffer[i] = (proc_fft_buffer[i * 2] * proc_fft_buffer[i * 2] - + proc_fft_buffer[i * 2 + 1] * proc_fft_buffer[i * 2 + 1]) - .sqrt(); - } - } - - /* do band stats */ - self.current_powers[0] = proc_fft_buffer[1..8].iter().sum::<f32>() / 8f32; - self.current_powers[1] = proc_fft_buffer[9..86].iter().sum::<f32>() / 78f32; - self.current_powers[2] = proc_fft_buffer[87..470].iter().sum::<f32>() / 384f32; - - for it in self.current_powers.iter().zip(self.avg_powers.iter_mut()) { - let (current, avg) = it; - *avg = falloff_f(*avg, *current); - } - - let last_fft_buf = self.next_fft_buf; - self.next_fft_buf = (self.next_fft_buf + 1) % AUDIO_BUFFERS; - last_fft_buf - } -} - -trait LedEffect { - fn render(&mut self, processed: &AudioProcessor, fft_buf: usize, leds: &LedColors); -} - -struct LedEffectBassSparks {} -impl LedEffect for LedEffectBassSparks { - fn render(&mut self, processed: &AudioProcessor, _fft_buf: usize, &(mut leds): &LedColors) { - let bass_color = if processed.floating_max > 10100000 - && (processed.current_powers[0] > 1.25 * processed.avg_powers[0]) - { - Rgbv::new(127, 0, 255, 4) - } else { - Rgbv::new(0, 0, 0, 0) - }; - - leds.fill(bass_color); - - bass_color.decrease(3, 5, 5, 0); - - if true - /*processed.floating_max > 10100000 - && (processed.current_powers[1] > 1.35 * processed.avg_powers[1]) - && (processed.current_powers[2] > 1.35 * processed.avg_powers[2])*/ - { - for _ in 0..10 { - let led_index = random_at_most(LED_COUNT as u32 - 1) as usize; - leds[led_index] = Rgbv::white(31); - } - } - } -} +use config::{AUDIO_BUFFERS, AUDIO_SAMPLES_PER_BUF, LED_COUNT}; +use effects::{ + bass_sparks::LedEffectBassSparks, + led_effect::{LedData, LedEffect}, +}; fn main() -> anyhow::Result<()> { // It is necessary to call this function once. Otherwise some patches to the runtime @@ -316,19 +40,13 @@ fn main() -> anyhow::Result<()> { let mut audio: [AudioBuffer; AUDIO_BUFFERS] = [[0; AUDIO_SAMPLES_PER_BUF]; AUDIO_BUFFERS]; let mut next_audio_buf: usize = 0; - // interfaces - let led_spi_per = peripherals.spi2; + + // i2s config let mic_i2s_per = peripherals.i2s0; - - // pins - let led_spi_sdo = peripherals.pins.gpio11; - let led_spi_sck = peripherals.pins.gpio12; - let mic_i2s_sd = peripherals.pins.gpio5; let mic_i2s_sclk = peripherals.pins.gpio4; let mic_i2s_ws = peripherals.pins.gpio6; - // i2s config let mic_i2s_std_cfg = i2s::config::StdConfig::new( i2s::config::Config::new().role(i2s::config::Role::Controller), i2s::config::StdClkConfig::new( @@ -342,7 +60,10 @@ fn main() -> anyhow::Result<()> { ) .data_bit_width(i2s::config::DataBitWidth::Bits32) .slot_bit_width(i2s::config::SlotBitWidth::Bits32) - .slot_mode_mask(i2s::config::SlotMode::Stereo, i2s::config::StdSlotMask::Both) + .slot_mode_mask( + i2s::config::SlotMode::Stereo, + i2s::config::StdSlotMask::Both, + ) .ws_width(32) .ws_polarity(false) .bit_shift(true) @@ -361,6 +82,10 @@ fn main() -> anyhow::Result<()> { )?; // spi config + let led_spi_per = peripherals.spi2; + let led_spi_sdo = peripherals.pins.gpio11; + let led_spi_sck = peripherals.pins.gpio12; + let mut led_drv = spi::SpiDeviceDriver::new_single( led_spi_per, led_spi_sck, @@ -385,43 +110,27 @@ fn main() -> anyhow::Result<()> { } let mut processor = AudioProcessor::new(); - let mut effect = LedEffectBassSparks {}; - - // loop { - // leds.leds[0] = Rgbv::red(4); - // let output_buffer = bytes_of(&leds); - // led_drv.write(output_buffer)?; - - // FreeRtos::delay_ms(10); - // } + let mut effect = LedEffectBassSparks::new(); mic_drv.rx_enable()?; + loop { - // let buffer: &mut [u8; AUDIO_SAMPLES_PER_BUF*4] = cast_slice_mut(&mut audio[next_audio_buf]); let buffer = bytes_of_mut(&mut audio[next_audio_buf]); - // let mut buffer:[u8;AUDIO_SAMPLES_PER_BUF*4] = [0;AUDIO_SAMPLES_PER_BUF*4]; - let num_bytes_read = mic_drv.read(buffer.as_mut_slice(), TickType_t::MAX)?; + let num_bytes_read = mic_drv.read(buffer, TickType_t::MAX)?; if num_bytes_read != AUDIO_SAMPLES_PER_BUF * 4 { log::error!("buffer underflow"); } - // for i in 0..AUDIO_SAMPLES_PER_BUF { - // let sample:&[u8;4] = &buffer[i*4..i*4+4].try_into().expect("bla"); - // audio[next_audio_buf][i] = i32::from_le_bytes(*sample); - // } - - // log::info!("a: {:08x}", audio[next_audio_buf][0]); - let current_fft_buf = processor.process(&audio[next_audio_buf]); - effect.render(&processor, current_fft_buf, &(leds.leds)); + let delay_ms: MilliSeconds = effect.render(&processor, current_fft_buf, &mut leds.leds); let output_buffer = bytes_of(&leds); led_drv.write(output_buffer)?; next_audio_buf = (next_audio_buf + 1) % AUDIO_BUFFERS; - FreeRtos::delay_ms(10); + FreeRtos::delay_ms(delay_ms.into()); } }