From 812a389ea22e45e9f70aecd9896a10b76a2a791a Mon Sep 17 00:00:00 2001
From: Patrick Moessler <pub@asaril.de>
Date: Mon, 24 Mar 2025 03:29:20 +0100
Subject: [PATCH] add runner effect

---
 src/effects/bass_hue_runner.rs | 86 ++++++++++++++++++++++++++++++++++
 src/effects/led_effect.rs      | 17 +++++++
 src/effects/mod.rs             |  3 +-
 src/main.rs                    |  3 +-
 4 files changed, 107 insertions(+), 2 deletions(-)
 create mode 100644 src/effects/bass_hue_runner.rs

diff --git a/src/effects/bass_hue_runner.rs b/src/effects/bass_hue_runner.rs
new file mode 100644
index 0000000..3b81e28
--- /dev/null
+++ b/src/effects/bass_hue_runner.rs
@@ -0,0 +1,86 @@
+use embassy_sync::blocking_mutex::raw::NoopRawMutex;
+use embassy_time::{Duration, Ticker};
+
+use crate::audio_process::{AudioStats, DspBuffer};
+use crate::config::LED_COUNT;
+use crate::effects::led_effect::{LedColors, LedData, LedEffect, Rgbv};
+use crate::triple_buffer::{Receiver, Sender};
+
+pub struct LedEffectBassHueRunner {
+    bass_color_hue: u32,
+    bass_color_fill: Rgbv,
+    bass_color_invert: bool,
+}
+impl Default for LedEffectBassHueRunner {
+    fn default() -> Self {
+        Self {
+            bass_color_hue: 300,
+            bass_color_fill: Rgbv::black(0),
+            bass_color_invert: false,
+        }
+    }
+}
+impl LedEffect for LedEffectBassHueRunner {
+    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_invert = !self.bass_color_invert;
+            self.bass_color_fill = Rgbv::from_hsv(
+                if self.bass_color_invert {
+                    (self.bass_color_hue + 180) % 360
+                } else {
+                    self.bass_color_hue
+                },
+                100,
+                100,
+                1,
+            )
+            .unwrap();
+        }
+
+        for i in 0..LED_COUNT / 2 - 1 {
+            leds[LED_COUNT / 2 + i] = leds[LED_COUNT / 2 + i + 1];
+            leds[LED_COUNT / 2 - 1 - i] = leds[LED_COUNT / 2 - 1 - i - 1];
+        }
+        leds[0] = self.bass_color_fill;
+        leds[LED_COUNT - 1] = 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(100)
+    }
+
+    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/led_effect.rs b/src/effects/led_effect.rs
index 8674aca..033c4a2 100644
--- a/src/effects/led_effect.rs
+++ b/src/effects/led_effect.rs
@@ -101,6 +101,23 @@ impl Rgbv {
         *self
     }
 
+    #[inline(always)]
+    pub fn divide(&mut self, r: u8, g: u8, b: u8, o: u8) -> Self {
+        self.r /= r;
+        self.g /= g;
+        self.b /= b;
+        self.set_o(self.o() / o);
+        *self
+    }
+
+    #[inline(always)]
+    pub fn divide_rgb(&mut self, r: u8, g: u8, b: u8) -> Self {
+        self.r /= r;
+        self.g /= g;
+        self.b /= b;
+        *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> {
diff --git a/src/effects/mod.rs b/src/effects/mod.rs
index 6511360..d15669c 100644
--- a/src/effects/mod.rs
+++ b/src/effects/mod.rs
@@ -1,4 +1,5 @@
 pub mod led_effect;
 pub mod bass_sparks;
 pub mod bass_hi_beat_strobe;
-pub mod bass_hue_fade;
\ No newline at end of file
+pub mod bass_hue_fade;
+pub mod bass_hue_runner;
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 4e778f1..fd83eab 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -20,7 +20,8 @@ use audio_process::{process_audio, AudioBuffer, AudioStats, DspBuffer};
 use config::{AUDIO_SAMPLES_PER_BUF, LED_COUNT};
 use effects::{
     // bass_sparks::LedEffectBassSparks as LedSelectedEffect,
-    bass_hue_fade::LedEffectBassHueFade as LedSelectedEffect,
+    // bass_hue_fade::LedEffectBassHueFade as LedSelectedEffect,
+    bass_hue_runner::LedEffectBassHueRunner as LedSelectedEffect,
     led_effect::{LedData, LedEffect},
 };
 use led_output::output_leds;