From dd157ce29de0d6d560910a92f05d6c7af31b9db8 Mon Sep 17 00:00:00 2001
From: David Huss <dh@atoav.com>
Date: Fri, 2 Feb 2024 17:44:41 +0100
Subject: [PATCH] Add Performance Effects

This adds three buttons in the Play Menu that can be performed live
with:
- Restart: Retriggers the loop, essentially jumps back to the start
- Slow Down: A vinyl-style slow down as long as the button is held
- Reverse: Reverse the playhead as long as the button is held
---
 code/daisy-looper/button_grid.h |  12 ++++----
 code/daisy-looper/looper.h      |  49 ++++++++++++++++++++++++++++++--
 code/daisy-looper/menu.ods      | Bin 20176 -> 20193 bytes
 code/daisy-looper/ui.h          |  29 ++++++++++++++++---
 4 files changed, 78 insertions(+), 12 deletions(-)

diff --git a/code/daisy-looper/button_grid.h b/code/daisy-looper/button_grid.h
index 78d78ff..96395e3 100644
--- a/code/daisy-looper/button_grid.h
+++ b/code/daisy-looper/button_grid.h
@@ -94,12 +94,12 @@ class ButtonGrid {
       for (int n=0; n<6; n++) {
         if (!grid_buttons_[n].is_home) {
           // Not a home button, display help on long hold and hide on release
-          grid_buttons_[n].button->onLongHold([this, n](){
-            grid_buttons_[n].should_render_description = true;
-          });
-          grid_buttons_[n].button->onReleased([this, n](){
-            grid_buttons_[n].should_render_description = false;
-          });
+          // grid_buttons_[n].button->onLongHold([this, n](){
+          //   grid_buttons_[n].should_render_description = true;
+          // });
+          // grid_buttons_[n].button->onReleased([this, n](){
+          //   grid_buttons_[n].should_render_description = false;
+          // });
         }
       }
     }
diff --git a/code/daisy-looper/looper.h b/code/daisy-looper/looper.h
index a9a0575..594ae1a 100644
--- a/code/daisy-looper/looper.h
+++ b/code/daisy-looper/looper.h
@@ -45,13 +45,18 @@ class Head {
     void deactivate();
     bool isActive();
     void setPosition(float value);
+    void reset();
     void setIncrement(float value);
     void incrementBy(float value);
+    void slowDown();
+    void reverse();
+    void speedUp();
     void update();
     float read();
     float increment = 1.0f;
     float variation = 0.0f;
     float variation_amount = 0.0f;
+    float speed_multiplier = 1.0f;
   private:
     bool active = true;
     float position = 0.0f;
@@ -69,8 +74,11 @@ void Head::deactivate() {
 bool Head::isActive() {
   return active;
 }
+void Head::reset() {
+  this->position = 0.0f;
+}
 void Head::setPosition(float value) {
-  this->position = value;
+  this->position = value * speed_multiplier;
 }
 void Head::setIncrement(float value) {
   this->increment = value;
@@ -78,8 +86,17 @@ void Head::setIncrement(float value) {
 void Head::incrementBy(float value) {
   this->position += value;
 }
+void Head::slowDown() {
+  this->speed_multiplier = max(0.0f, this->speed_multiplier - 0.0025f);
+}
+void Head::reverse() {
+  this->speed_multiplier = -abs(this->speed_multiplier );
+}
+void Head::speedUp() {
+  this->speed_multiplier = 1.0f;
+}
 void Head::update() {
-  this->position += (this->increment + (variation * variation_amount));
+  this->position += (this->increment + (variation * variation_amount)) * speed_multiplier;
 }
 float Head::read() {
   return this->position;
@@ -118,6 +135,10 @@ class Looper {
     void setRecModeLoopShot();
     void setPlaybackSpeed(float increment);
     void addToPlayhead(float value);
+    void slowDown();
+    void reverse();
+    void restart();
+    void speedUp();
     float loop_start_f = 0.0f;
     float loop_length_f = 1.0f;
     uint8_t grain_count = 8;
@@ -402,6 +423,30 @@ void Looper::addToPlayhead(float value) {
   }
 }
 
+void Looper::slowDown() {
+  for (size_t i=0; i<9; i++) {
+    playheads[i].slowDown();
+  }
+}
+
+void Looper::reverse() {
+  for (size_t i=0; i<9; i++) {
+    playheads[i].reverse();
+  }
+}
+
+void Looper::restart() {
+  for (size_t i=0; i<9; i++) {
+    playheads[i].reset();
+  }
+}
+
+void Looper::speedUp() {
+  for (size_t i=0; i<9; i++) {
+    playheads[i].speedUp();
+  }
+}
+
 float* Looper::getBuffer() {
   return buffer;
 }
diff --git a/code/daisy-looper/menu.ods b/code/daisy-looper/menu.ods
index b744707a290dc99f6530d3f577376ab5774bb22e..2b8391b9cf03447b0e7b7cf66da2c734faf8ed40 100644
GIT binary patch
delta 4607
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
Z1_n_S13!68{_7z=d6%C6TbdV01_1l|jeGzA

delta 4582
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

diff --git a/code/daisy-looper/ui.h b/code/daisy-looper/ui.h
index 19c4720..3dcb82e 100644
--- a/code/daisy-looper/ui.h
+++ b/code/daisy-looper/ui.h
@@ -114,9 +114,9 @@ class Ui {
         GridButton("STOP\nLOOP\nMULTI\nMIDI", &button_1, false, BUTTON_TYPE_MULTITOGGLE, 1),
         GridButton("PLAY\nMENU", &button_2, true),
         GridButton("ACTIVE\nSUM\nRING", &button_3, false, BUTTON_TYPE_MULTITOGGLE, 0),
-        GridButton(" ", &button_4, false),
-        GridButton(" ", &button_5, false),
-        GridButton(" ", &button_6, false),
+        GridButton("RE\nSTART", &button_4, false),
+        GridButton("SLOW\nDOWN", &button_5, false),
+        GridButton("REV\nERSE", &button_6, false),
       }),
       ButtonGrid((int) UI_MODE_TRIGGER_MENU, {
         GridButton("MIDI\nTRIG.", &button_1, false),
@@ -241,6 +241,7 @@ class Ui {
       home_button->onReleased([this, n](){
         this->setMode(UI_MODE_DEFAULT);
         this->button_grids[n].hideAllDescriptions();
+        activeLooper()->speedUp();
       });
 
       // Return pointer to the home button
@@ -375,7 +376,7 @@ class Ui {
         // Setup button Grid
         Button* home_button = setupButtonGrid(n);
 
-        // Chang the way in which buffers are summed
+        // Change the way in which buffers are summed
         button_3.onPress([this, n](){ 
           button_grids[n].grid_buttons_[2].next();
           buffer_summing_mode = (BufferSummingMode) button_grids[n].grid_buttons_[2].active;
@@ -387,6 +388,26 @@ class Ui {
           activeLooper()->playback_state = (atoav::PlaybackState) (button_grids[n].grid_buttons_[0].active);
         });
 
+        // Restart
+        button_4.onPress([this, n](){ 
+          activeLooper()->restart();
+        });
+
+        // DJ-style slow-down effect
+        button_5.onHold([this, n](){ 
+          activeLooper()->slowDown();
+        });
+        button_5.onReleased([this, n](){ 
+          activeLooper()->speedUp();
+        });
+
+        button_6.onHold([this, n](){ 
+          activeLooper()->reverse();
+        });
+        button_6.onReleased([this, n](){ 
+          activeLooper()->speedUp();
+        });
+
         // Store the last ui mode, for the check on top
         last_ui_mode = ui_mode;
       }
-- 
GitLab