From de2b0593aeca7da3d1e7aa962eee49635d3d194a Mon Sep 17 00:00:00 2001
From: Patrick Moessler <pub@asaril.de>
Date: Sat, 22 Mar 2025 00:59:47 +0100
Subject: [PATCH] separate effects

---
 src/effects/bass_hi_beat_strobe.rs | 114 +++++++++++++++++++++++++++++
 src/effects/bass_hue_fade.rs       |  67 +++++++++++++++++
 src/effects/bass_sparks.rs         |  23 ++++--
 src/effects/mod.rs                 |   4 +-
 src/main.rs                        |   7 +-
 5 files changed, 205 insertions(+), 10 deletions(-)
 create mode 100644 src/effects/bass_hi_beat_strobe.rs
 create mode 100644 src/effects/bass_hue_fade.rs

diff --git a/src/effects/bass_hi_beat_strobe.rs b/src/effects/bass_hi_beat_strobe.rs
new file mode 100644
index 0000000..c1d1bfc
--- /dev/null
+++ b/src/effects/bass_hi_beat_strobe.rs
@@ -0,0 +1,114 @@
+use embassy_sync::blocking_mutex::raw::NoopRawMutex;
+use embassy_time::{Duration, Ticker};
+
+use crate::audio_process::{AudioStats, DspBuffer};
+use crate::effects::led_effect::{LedColors, LedData, LedEffect, Rgbv};
+use crate::triple_buffer::{Receiver, Sender};
+use crate::LED_COUNT;
+
+pub struct LedEffectBassHiBeatStrobe {
+    bass_color_hue: u32,
+    bass_color_fill: Rgbv,
+    hi_beat: u8,
+}
+impl Default for LedEffectBassHiBeatStrobe {
+    fn default() -> Self {
+        Self {
+            bass_color_hue: 300,
+            bass_color_fill: Rgbv::black(0),
+            hi_beat: 0,
+        }
+    }
+}
+impl LedEffect for LedEffectBassHiBeatStrobe {
+    fn render(
+        &mut self,
+        _: bool,
+        _: &DspBuffer,
+        stats: &AudioStats,
+        leds: &mut LedColors,
+    ) -> Duration {
+        if stats.floating_max > 10100000 && (stats.current_powers[0] > 1.25 * stats.avg_powers[0]) {
+            self.bass_color_fill = Rgbv::from_hsv(self.bass_color_hue, 100, 100, 1).unwrap();
+        }
+
+        leds.fill(self.bass_color_fill);
+
+        // for i in 0..32 {
+        //     leds[i] = self.bass_color_fill.with_o(Rgbv::MAX_O - i as u8);
+        //     leds[LED_COUNT-1-i] = self.bass_color_fill.with_o(Rgbv::MAX_O - i as u8);
+        // }
+
+        self.bass_color_fill.decrease(
+            std::cmp::max(self.bass_color_fill.r / 4, 1),
+            std::cmp::max(self.bass_color_fill.g / 4, 1),
+            std::cmp::max(self.bass_color_fill.b / 4, 1),
+            0,
+        );
+
+        if stats.floating_max > 10100000
+            && (stats.current_powers[1] > 1.35 * stats.avg_powers[1])
+            && (stats.current_powers[2] > 1.35 * stats.avg_powers[2])
+        {
+            // for led_index in LED_COUNT / 2 - 4..LED_COUNT / 2 + 4 {
+            //     leds[led_index] = Rgbv::white(31);
+            // }
+
+            // let mut tmp = LED_COUNT / 2 - 4..LED_COUNT / 2 + 4;
+            // while let Some(led_index) = tmp.next() {
+            //     leds[led_index] = Rgbv::white(31);
+            // }
+
+            // for led in leds
+            //     .iter_mut()
+            //     .take(LED_COUNT / 2 + 4)
+            //     .skip(LED_COUNT / 2 - 4)
+            // {
+            //     *led = Rgbv::white(31);
+            // }
+
+            // for _ in 0..20 {
+            //     let led_index = random_at_most(LED_COUNT as u32 - 1) as usize;
+            //     leds[led_index] = Rgbv::white(31);
+            // }
+            self.hi_beat = 6;
+        }
+
+        if self.hi_beat > 0 {
+            (LED_COUNT / 2 - 8..LED_COUNT / 2 + 8).for_each(|led_index| {
+                leds[led_index] = if self.hi_beat % 2 == 0 {
+                    Rgbv::black(0)
+                } else {
+                    Rgbv::white(31)
+                }
+            });
+            self.hi_beat -= 1;
+        }
+
+        self.bass_color_hue = (self.bass_color_hue + 1) % 360;
+
+        Duration::from_hz(50)
+    }
+
+    async fn process_led_effect(
+        &mut self,
+        mut input: Receiver<'static, NoopRawMutex, (DspBuffer, AudioStats)>,
+        mut output: Sender<'static, NoopRawMutex, LedData>,
+    ) {
+        let mut set_interval = Duration::from_hz(100);
+        let mut ticker = Ticker::every(set_interval);
+        loop {
+            let ((fft, stats), was_new) = input.receive_cached().await;
+            let leds = output.send().await;
+
+            let interval = self.render(was_new, fft, stats, &mut leds.leds);
+            output.send_done();
+
+            if set_interval != interval {
+                set_interval = interval;
+                ticker = Ticker::every(set_interval);
+            }
+            ticker.next().await;
+        }
+    }
+}
diff --git a/src/effects/bass_hue_fade.rs b/src/effects/bass_hue_fade.rs
new file mode 100644
index 0000000..272a84a
--- /dev/null
+++ b/src/effects/bass_hue_fade.rs
@@ -0,0 +1,67 @@
+use embassy_sync::blocking_mutex::raw::NoopRawMutex;
+use embassy_time::{Duration, Ticker};
+
+use crate::audio_process::{AudioStats, DspBuffer};
+use crate::effects::led_effect::{LedColors, LedData, LedEffect, Rgbv};
+use crate::triple_buffer::{Receiver, Sender};
+
+pub struct LedEffectBassHueFade {
+    bass_color_hue: u32,
+    bass_color_fill: Rgbv,
+}
+impl Default for LedEffectBassHueFade {
+    fn default() -> Self {
+        Self {
+            bass_color_hue: 300,
+            bass_color_fill: Rgbv::black(0),
+        }
+    }
+}
+impl LedEffect for LedEffectBassHueFade {
+    fn render(
+        &mut self,
+        _: bool,
+        _: &DspBuffer,
+        stats: &AudioStats,
+        leds: &mut LedColors,
+    ) -> Duration {
+        if stats.floating_max > 10100000 && (stats.current_powers[0] > 1.25 * stats.avg_powers[0]) {
+            self.bass_color_fill = Rgbv::from_hsv(self.bass_color_hue, 100, 100, 1).unwrap();
+        }
+
+        leds.fill(self.bass_color_fill);
+
+        self.bass_color_fill.decrease(
+            std::cmp::max(self.bass_color_fill.r / 4, 1),
+            std::cmp::max(self.bass_color_fill.g / 4, 1),
+            std::cmp::max(self.bass_color_fill.b / 4, 1),
+            0,
+        );
+
+        self.bass_color_hue = (self.bass_color_hue + 1) % 360;
+
+        Duration::from_hz(50)
+    }
+
+    async fn process_led_effect(
+        &mut self,
+        mut input: Receiver<'static, NoopRawMutex, (DspBuffer, AudioStats)>,
+        mut output: Sender<'static, NoopRawMutex, LedData>,
+    ) {
+        let mut set_interval = Duration::from_hz(100);
+        let mut ticker = Ticker::every(set_interval);
+        loop {
+            let ((fft, stats), was_new) = input.receive_cached().await;
+            let leds = output.send().await;
+
+            let interval = self.render(was_new, fft, stats, &mut leds.leds);
+            output.send_done();
+
+            if set_interval != interval {
+                set_interval = interval;
+                ticker = Ticker::every(set_interval);
+            }
+            ticker.next().await;
+        }
+    }
+}
diff --git a/src/effects/bass_sparks.rs b/src/effects/bass_sparks.rs
index 598745b..c044a7d 100644
--- a/src/effects/bass_sparks.rs
+++ b/src/effects/bass_sparks.rs
@@ -8,12 +8,14 @@ use crate::triple_buffer::{Receiver, Sender};
 use crate::LED_COUNT;
 
 pub struct LedEffectBassSparks {
-    bass_color: Rgbv,
+    bass_color_hue: u32,
+    bass_color_fill: Rgbv,
 }
 impl Default for LedEffectBassSparks {
     fn default() -> Self {
         Self {
-            bass_color: Rgbv::black(0),
+            bass_color_hue: 300,
+            bass_color_fill: Rgbv::black(0),
         }
     }
 }
@@ -26,22 +28,31 @@ impl LedEffect for LedEffectBassSparks {
         leds: &mut LedColors,
     ) -> Duration {
         if stats.floating_max > 10100000 && (stats.current_powers[0] > 1.25 * stats.avg_powers[0]) {
-            self.bass_color = Rgbv::new(127, 0, 255, 4)
+            self.bass_color_fill = Rgbv::from_hsv(self.bass_color_hue, 100, 100, 1).unwrap();
         }
 
-        leds.fill(self.bass_color);
+        leds.fill(self.bass_color_fill);
 
-        self.bass_color.decrease(10, 15, 20, 0);
+        self.bass_color_fill.decrease(
+            std::cmp::max(self.bass_color_fill.r / 4, 1),
+            std::cmp::max(self.bass_color_fill.g / 4, 1),
+            std::cmp::max(self.bass_color_fill.b / 4, 1),
+            0,
+        );
 
         if stats.floating_max > 10100000
             && (stats.current_powers[1] > 1.35 * stats.avg_powers[1])
             && (stats.current_powers[2] > 1.35 * stats.avg_powers[2])
         {
+            let alternate_color =
+                Rgbv::from_hsv((self.bass_color_hue + 180) % 360, 100, 100, 5).unwrap();
             for _ in 0..20 {
                 let led_index = random_at_most(LED_COUNT as u32 - 1) as usize;
-                leds[led_index] = Rgbv::white(31);
+                // leds[led_index] = Rgbv::white(31);
+                leds[led_index] = alternate_color;
             }
         }
+        self.bass_color_hue = (self.bass_color_hue + 1) % 360;
 
         Duration::from_hz(50)
     }
diff --git a/src/effects/mod.rs b/src/effects/mod.rs
index 3faa122..6511360 100644
--- a/src/effects/mod.rs
+++ b/src/effects/mod.rs
@@ -1,2 +1,4 @@
 pub mod led_effect;
-pub mod bass_sparks;
\ No newline at end of file
+pub mod bass_sparks;
+pub mod bass_hi_beat_strobe;
+pub mod bass_hue_fade;
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index eaa45bd..f29fb27 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -19,7 +19,8 @@ use audio_input::mic_input_task;
 use audio_process::{process_audio, AudioBuffer, AudioStats, DspBuffer};
 use config::{AUDIO_SAMPLES_PER_BUF, LED_COUNT};
 use effects::{
-    bass_sparks::LedEffectBassSparks,
+    // bass_sparks::LedEffectBassSparks as LedSelectedEffect,
+    bass_hue_fade::LedEffectBassHueFade as LedSelectedEffect,
     led_effect::{LedData, LedEffect},
 };
 use led_output::output_leds;
@@ -27,7 +28,7 @@ use triple_buffer::{Receiver, Sender, TripleBuffer};
 
 #[embassy_executor::task]
 async fn effect_task(
-    mut led_effect: LedEffectBassSparks,
+    mut led_effect: LedSelectedEffect,
     input: Receiver<'static, NoopRawMutex, (DspBuffer, AudioStats)>,
     output: Sender<'static, NoopRawMutex, LedData>,
 ) {
@@ -99,7 +100,7 @@ async fn main(spawner: Spawner) {
 
     spawner
         .spawn(effect_task(
-            LedEffectBassSparks::default(),
+            LedSelectedEffect::default(),
             fft_receiver,
             led_sender,
         ))