From 3a4a02a1386f558f08594679dee1cbb215d148b0 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Sun, 8 Dec 2024 20:02:05 +0100
Subject: [PATCH 01/12] sust wip

---
 guitfx.c    | 17 +++++++++++-
 meson.build |  7 ++++-
 sust.c      | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 sust.h      | 31 +++++++++++++++++++++
 4 files changed, 133 insertions(+), 2 deletions(-)
 create mode 100644 sust.c
 create mode 100644 sust.h

diff --git a/guitfx.c b/guitfx.c
index 02f8a12..4ab88e1 100644
--- a/guitfx.c
+++ b/guitfx.c
@@ -14,7 +14,9 @@
 #include <pipewire/filter.h>
 
 #include "delay.h"
+#include "sust.h"
 
+float envelope( float x );
 struct data;
 
 struct port {
@@ -31,6 +33,7 @@ struct data {
 	//! effect data
 	uint64_t last_tap;
 	struct delay delay;
+	struct sust sust;
 
 	//! elapsed time in number of samples
 	uint64_t time;
@@ -90,7 +93,17 @@ static void on_process(void *userdata, struct spa_io_position *position)
 
 								    case 0x42:
 										// sust pedal
+										if( midi_data[2] >= 64) {
+											sust_swap( &data->sust );
+										    data->sust.playing = true;
 
+											data->sust.start_idx = data->sust.idx;
+											data->sust.idx = 0;
+										} else {
+											data->sust.playing = false;
+										}
+
+										// tap tempo
    									    uint64_t cur_tap = frame + c->offset;
     									uint64_t duration = cur_tap - data->last_tap;
     									data->last_tap = cur_tap;
@@ -126,7 +139,8 @@ static void on_process(void *userdata, struct spa_io_position *position)
 	float * const in = pw_filter_get_dsp_buffer(data->guit_in_port, n_samples);
 	float * out = pw_filter_get_dsp_buffer(data->out_port, n_samples);
 	if( in && out ) {
-        delay_process( &data->delay, n_samples, in, out );
+        //delay_process( &data->delay, n_samples, in, out );
+        sust_process( &data->sust, n_samples, in, out );
 	}
 }
 
@@ -146,6 +160,7 @@ int main(int argc, char *argv[])
 	struct data data = { 0, };
 
 	delay_init( &data.delay );
+	sust_init( &data.sust );
 
 	const struct spa_pod *params[1];
 	uint8_t buffer[1024];
diff --git a/meson.build b/meson.build
index 5f61c36..9fc0dd8 100644
--- a/meson.build
+++ b/meson.build
@@ -1,8 +1,13 @@
 project('guitfx', 'c')
 pipewire_dep = dependency('libpipewire-0.3')
+
+cc = meson.get_compiler('c')
+m_dep = cc.find_library('m', required : false)
+
 executable(
     'guitfx',
     'guitfx.c',
     'delay.c',
-    dependencies : [pipewire_dep],
+    'sust.c',
+    dependencies : [pipewire_dep, m_dep],
 )
diff --git a/sust.c b/sust.c
new file mode 100644
index 0000000..c836b2a
--- /dev/null
+++ b/sust.c
@@ -0,0 +1,80 @@
+#include "sust.h"
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+void sust_init( struct sust * sust ) {
+    sust->mode = MODE_Sostenuto;
+
+    sust->playing = false;
+
+    sust->start_idx = 0;
+    sust->idx = 0;
+
+    sust->buf_len = 12800;
+    sust->record_buf = malloc( sizeof(float) * sust->buf_len );
+    sust->play_buf = malloc( sizeof(float) * sust->buf_len );
+}
+
+float envelope( float x ) {
+    if( x < 0.4 )
+        return (x / 0.4) * (x / 0.4);
+    if( x < 0.6 )
+        return 1.0;
+
+   float v  = 1.0 - ((x - 0.6) / 0.4);
+   return v*v;
+}
+
+float softcos( float x ) {
+    return sin( x * (3.141 / 2.0) );
+}
+
+void sust_swap(
+    struct sust * sust
+) {
+    float * tmp = sust->play_buf;
+    sust->play_buf = sust->record_buf;
+    sust->record_buf = tmp;
+
+    for( int i = 0; i  < sust->buf_len; ++i ) {
+        sust->record_buf[i] = sust->play_buf[i];
+    }
+}
+
+void sust_process(
+    struct sust * sust,
+    size_t frame_size,
+    float const * in,
+    float * out
+) {
+    for( size_t i = 0; i < frame_size; ++i ) {
+
+        if( sust->mode == MODE_Sustain && sust->idx == 0 ) {
+            sust_swap( sust );
+        }
+
+        float out_value = in[i];
+
+        if( sust->playing ) {
+            int n_voices = 5;
+
+            for( int v = 0; v < n_voices; ++v ) {
+                size_t play_idx = (sust->start_idx + sust->idx + ((v*sust->buf_len)/n_voices)) % sust->buf_len;
+                float gain = envelope( ((float) play_idx) / ((float)sust->buf_len) );
+               //printf("gain = %f\n", gain);
+                out_value += 0.5 *gain * sust->play_buf[ play_idx ];
+            }
+        }
+
+        if( sust->mode == MODE_Sostenuto ) {
+            sust->record_buf[ sust->idx ] = in[i];
+        }
+        if( sust->mode == MODE_Sustain ) {
+            sust->record_buf[ sust->idx ] = 0.5*out_value;
+        }
+
+        sust->idx = (sust->idx + 1) % sust->buf_len;
+        out[i] = out_value;
+    }
+}
diff --git a/sust.h b/sust.h
new file mode 100644
index 0000000..23c6f67
--- /dev/null
+++ b/sust.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+
+enum sust_mode {
+    MODE_Sustain = 0,
+    MODE_Sostenuto = 1
+};
+
+struct sust {
+    enum sust_mode mode;
+    bool playing;
+
+    size_t start_idx;
+    size_t idx;
+    size_t buf_len;
+    float * record_buf;
+    float * play_buf;
+};
+
+
+void sust_init( struct sust * sust );
+void sust_swap( struct sust * sust );
+
+void sust_process(
+    struct sust * sust,
+    size_t frame_size,
+    float const * in,
+    float * out
+);

From 52f09d772858d13cb5e030fb757f623ad93c418e Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Fri, 13 Dec 2024 12:59:33 +0100
Subject: [PATCH 02/12] sust: rename buffer

---
 sust.c | 9 ++++++++-
 sust.h | 1 +
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/sust.c b/sust.c
index c836b2a..d684930 100644
--- a/sust.c
+++ b/sust.c
@@ -11,7 +11,7 @@ void sust_init( struct sust * sust ) {
     sust->start_idx = 0;
     sust->idx = 0;
 
-    sust->buf_len = 12800;
+    sust->buf_len = 51200;
     sust->record_buf = malloc( sizeof(float) * sust->buf_len );
     sust->play_buf = malloc( sizeof(float) * sust->buf_len );
 }
@@ -30,6 +30,13 @@ float softcos( float x ) {
     return sin( x * (3.141 / 2.0) );
 }
 
+void sust_resize( struct sust * sust, size_t new_len ) {
+  sust->buf_len = new_len;
+  sust->play_buf = realloc( sust->play_buf, sizeof(float) * new_len );
+  sust->record_buf = realloc( sust->record_buf, sizeof(float) * new_len );
+  sust->idx %= new_len;
+}
+
 void sust_swap(
     struct sust * sust
 ) {
diff --git a/sust.h b/sust.h
index 23c6f67..92bece8 100644
--- a/sust.h
+++ b/sust.h
@@ -21,6 +21,7 @@ struct sust {
 
 
 void sust_init( struct sust * sust );
+void sust_resize( struct sust * sust, size_t new_len );
 void sust_swap( struct sust * sust );
 
 void sust_process(

From c75d61123a774e68d3744718e5fbc59d813d2ed6 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Fri, 13 Dec 2024 13:00:13 +0100
Subject: [PATCH 03/12] use program change to switch function of sust pedal
 (switch between tap tempo / activate sustain

---
 delay.c  |  4 ++--
 guitfx.c | 37 +++++++++++++++++++++++++++----------
 2 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/delay.c b/delay.c
index c99b818..31f3089 100644
--- a/delay.c
+++ b/delay.c
@@ -44,8 +44,8 @@ void delay_process(
     for( size_t i = 0; i < frame_size; ++i ) {
         if( delay->duration > 0 ) {
             out[i] =
-                0.5 * in[i]
-                + 0.5 * delay->mix * delay->buf[ delay->buf_idx ];
+                in[i]
+                + delay->mix * delay->buf[ delay->buf_idx ];
 
             delay->buf[ delay->buf_idx ] *= delay->feedback;
             delay->buf[ delay->buf_idx ] += (1.0 - delay->feedback) * in[i];
diff --git a/guitfx.c b/guitfx.c
index 4ab88e1..052e37a 100644
--- a/guitfx.c
+++ b/guitfx.c
@@ -35,6 +35,8 @@ struct data {
 	struct delay delay;
 	struct sust sust;
 
+        unsigned prog;
+
 	//! elapsed time in number of samples
 	uint64_t time;
 };
@@ -87,12 +89,16 @@ static void on_process(void *userdata, struct spa_io_position *position)
 									    // expr pedal
 										float val_f = ((float)midi_data[2]) / 128.0;
 										printf("Expr Pedal %f\n", val_f);
-
-										data->delay.mix = val_f;
+										if ( val_f > 0.1 ) {
+										  data->delay.mix = val_f;
+										} else {
+										  data->delay.mix = 0.0;
+										}
 									    break;
 
 								    case 0x42:
 										// sust pedal
+								      if( data->prog == 1 ) {
 										if( midi_data[2] >= 64) {
 											sust_swap( &data->sust );
 										    data->sust.playing = true;
@@ -102,21 +108,30 @@ static void on_process(void *userdata, struct spa_io_position *position)
 										} else {
 											data->sust.playing = false;
 										}
+								      }
 
 										// tap tempo
-   									    uint64_t cur_tap = frame + c->offset;
-    									uint64_t duration = cur_tap - data->last_tap;
-    									data->last_tap = cur_tap;
-    									if( duration < (4*position->clock.rate.denom) ) {
-    									  delay_set_time( &data->delay, duration/4 );
-    									}
+										if( data->prog == 0 ) {
+										  uint64_t cur_tap = frame + c->offset;
+										  uint64_t duration = cur_tap - data->last_tap;
+										  data->last_tap = cur_tap;
+										  if( duration < (4*position->clock.rate.denom) ) {
+										    delay_set_time( &data->delay, duration );
+										  }
+
+
+										  sust_resize( &data->sust, duration );
+										}
+
 										break;
 								}
+
 								break;
 
 							case 0xc0:
 							    // program change
 								printf("program change: %u\n", midi_data[1]);
+								data->prog = midi_data[1];
 							    break;
 						}
 					} else {
@@ -137,10 +152,11 @@ static void on_process(void *userdata, struct spa_io_position *position)
 
 
 	float * const in = pw_filter_get_dsp_buffer(data->guit_in_port, n_samples);
+	float tmp[n_samples];
 	float * out = pw_filter_get_dsp_buffer(data->out_port, n_samples);
 	if( in && out ) {
-        //delay_process( &data->delay, n_samples, in, out );
-        sust_process( &data->sust, n_samples, in, out );
+	  sust_process( &data->sust, n_samples, in, tmp );
+	  delay_process( &data->delay, n_samples, tmp, out );
 	}
 }
 
@@ -161,6 +177,7 @@ int main(int argc, char *argv[])
 
 	delay_init( &data.delay );
 	sust_init( &data.sust );
+	data.prog = 0;
 
 	const struct spa_pod *params[1];
 	uint8_t buffer[1024];

From bcead0e0b5034de1d78f9cfa79505815385a473b Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Sun, 12 Jan 2025 05:07:05 +0100
Subject: [PATCH 04/12] expr pedal threshold

---
 guitfx.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/guitfx.c b/guitfx.c
index 052e37a..a512079 100644
--- a/guitfx.c
+++ b/guitfx.c
@@ -87,13 +87,16 @@ static void on_process(void *userdata, struct spa_io_position *position)
 								switch( midi_data[1] ) {
 									case 0x0b:
 									    // expr pedal
-										float val_f = ((float)midi_data[2]) / 128.0;
-										printf("Expr Pedal %f\n", val_f);
-										if ( val_f > 0.1 ) {
-										  data->delay.mix = val_f;
-										} else {
-										  data->delay.mix = 0.0;
-										}
+							      		    float val_f = ((float)midi_data[2]) / 128.0;
+
+									    float thres = 0.4;
+									    
+								            if ( val_f > thres ) {
+									        printf("Expr Pedal %f\n", val_f);
+										data->delay.mix = (val_f - thres) / (1.0-thres);
+									    } else {
+									        data->delay.mix = 0.0;
+								            }
 									    break;
 
 								    case 0x42:

From 7e5a01f9592be7bfe21b0cb8e534f4c983914ae6 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Sun, 12 Jan 2025 05:08:18 +0100
Subject: [PATCH 05/12] wip noise gate

---
 gate.c      | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gate.h      | 29 ++++++++++++++++++
 guitfx.c    | 32 +++++++++++++++++---
 meson.build |  1 +
 4 files changed, 143 insertions(+), 4 deletions(-)
 create mode 100644 gate.c
 create mode 100644 gate.h

diff --git a/gate.c b/gate.c
new file mode 100644
index 0000000..9eb0949
--- /dev/null
+++ b/gate.c
@@ -0,0 +1,85 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "gate.h"
+#include <math.h>
+
+void gate_init(
+    struct gate * gate
+) {
+  
+    gate->threshold = 0.0;
+    gate->enable_calibration = false;
+
+    gate->block_size = 1024;
+    gate->hist_size = 256;
+    gate->hist_idx = 0;
+    gate->hist = malloc( sizeof(float) * gate->hist_size );
+    gate->cur_block_count = 0;
+    gate->cur_block_sum = 0.0;    
+}
+
+void gate_process(
+    struct gate * gate,
+    size_t frame_size,
+    float const * in,
+    float * out
+) {
+    float sum = 0.0;
+
+
+    int last_frame = 0;
+
+    for( size_t i = 0; i < frame_size; ++i ) {
+        gate->cur_block_sum += fabs( in[i] );
+        gate->cur_block_count += 1;
+
+        if( gate->cur_block_count >= gate->block_size ) {
+	  printf("new block sum [%lu] (frame %lu) : %f\n", gate->hist_idx, i, gate->cur_block_sum);
+
+	  if( gate->cur_block_sum > 20.0 ) {
+	    gate->is_active = true;
+	  } else {
+	    gate->is_active = false;
+	  }
+
+            gate->hist[ gate->hist_idx ] = gate->cur_block_sum;
+	    gate->hist_idx = (gate->hist_idx + 1) % gate->hist_size;
+            gate->cur_block_sum = 0.0;
+	    gate->cur_block_count = 0;
+
+
+	    last_frame = i;
+	}
+    }
+
+    unsigned enable_pos = gate->is_active ? 0 : frame_size;
+    unsigned disable_pos = gate->is_active ? frame_size : 0;
+
+    unsigned blocks_per_buffer = frame_size / gate->block_size;
+
+    /* start from last block sum and iterate backwards to find
+     * the timepoint where a block sum crosses the threshold
+     */
+    /*
+    unsigned i = gate->hist_idx;
+    while( last_frame > 0 ) {
+        if( gate->hist[ i ] < gate->threshold ) {
+            disable_pos = last_frame;
+        }
+
+        last_frame -= gate->block_size;
+        i -= 1;
+    }
+    */
+
+
+    for( size_t i = 0; i < frame_size; ++i ) {
+      //        if( i >= enable_pos && i < disable_pos )
+        if( gate->is_active )
+            out[i] = in[i];
+	else
+	    out[i] = 0.0;
+    }  
+}
diff --git a/gate.h b/gate.h
new file mode 100644
index 0000000..9456ccd
--- /dev/null
+++ b/gate.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+struct gate {
+  float threshold;
+  bool enable_calibration;
+
+  unsigned block_size;
+
+  unsigned hist_idx;
+  unsigned hist_size;
+  float * hist;
+
+  unsigned cur_block_count;
+  float cur_block_sum;
+
+  bool is_active;
+};
+
+void gate_init();
+void gate_process(
+     struct gate * gate,
+     size_t frame_size,
+     float const * in,
+     float * out
+);
diff --git a/guitfx.c b/guitfx.c
index a512079..824310a 100644
--- a/guitfx.c
+++ b/guitfx.c
@@ -15,8 +15,10 @@
 
 #include "delay.h"
 #include "sust.h"
+#include "gate.h"
 
 float envelope( float x );
+
 struct data;
 
 struct port {
@@ -34,6 +36,7 @@ struct data {
 	uint64_t last_tap;
 	struct delay delay;
 	struct sust sust;
+        struct gate gate;
 
         unsigned prog;
 
@@ -100,7 +103,16 @@ static void on_process(void *userdata, struct spa_io_position *position)
 									    break;
 
 								    case 0x42:
-										// sust pedal
+
+								      // noise gate calibration
+								      if( data->prog == 2 ) {
+									if( midi_data[2] >= 64) {
+									  data->gate.threshold = 0.0;//data->gate.cur_avg;
+									  printf("calibrate noise gate: threshold = %f\n", data->gate.threshold); 
+									}
+								      }
+
+							  	      // sust pedal
 								      if( data->prog == 1 ) {
 										if( midi_data[2] >= 64) {
 											sust_swap( &data->sust );
@@ -133,6 +145,15 @@ static void on_process(void *userdata, struct spa_io_position *position)
 
 							case 0xc0:
 							    // program change
+										if( midi_data[2] >= 64) {
+											sust_swap( &data->sust );
+										    data->sust.playing = true;
+
+											data->sust.start_idx = data->sust.idx;
+											data->sust.idx = 0;
+										} else {
+											data->sust.playing = false;
+										}
 								printf("program change: %u\n", midi_data[1]);
 								data->prog = midi_data[1];
 							    break;
@@ -155,11 +176,13 @@ static void on_process(void *userdata, struct spa_io_position *position)
 
 
 	float * const in = pw_filter_get_dsp_buffer(data->guit_in_port, n_samples);
-	float tmp[n_samples];
+	float tmp1[n_samples];
+	float tmp2[n_samples];
 	float * out = pw_filter_get_dsp_buffer(data->out_port, n_samples);
 	if( in && out ) {
-	  sust_process( &data->sust, n_samples, in, tmp );
-	  delay_process( &data->delay, n_samples, tmp, out );
+	  gate_process( &data->gate, n_samples, in, tmp1 );
+	  sust_process( &data->sust, n_samples, tmp1, tmp2 );
+	  delay_process( &data->delay, n_samples, tmp2, out );
 	}
 }
 
@@ -178,6 +201,7 @@ int main(int argc, char *argv[])
 {
 	struct data data = { 0, };
 
+	gate_init( &data.gate );
 	delay_init( &data.delay );
 	sust_init( &data.sust );
 	data.prog = 0;
diff --git a/meson.build b/meson.build
index 9fc0dd8..d59ef2b 100644
--- a/meson.build
+++ b/meson.build
@@ -9,5 +9,6 @@ executable(
     'guitfx.c',
     'delay.c',
     'sust.c',
+    'gate.c',
     dependencies : [pipewire_dep, m_dep],
 )

From f4bbf94e30fb54e16bfd9fc2cb54284cd885e73b Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Mon, 27 Jan 2025 21:27:49 +0100
Subject: [PATCH 06/12] noise gate: simple attack&release envelope

---
 gate.c | 37 ++++++++++++++++++++++++-------------
 gate.h |  4 ++++
 2 files changed, 28 insertions(+), 13 deletions(-)

diff --git a/gate.c b/gate.c
index 9eb0949..a25a39c 100644
--- a/gate.c
+++ b/gate.c
@@ -8,7 +8,6 @@
 void gate_init(
     struct gate * gate
 ) {
-  
     gate->threshold = 0.0;
     gate->enable_calibration = false;
 
@@ -17,7 +16,12 @@ void gate_init(
     gate->hist_idx = 0;
     gate->hist = malloc( sizeof(float) * gate->hist_size );
     gate->cur_block_count = 0;
-    gate->cur_block_sum = 0.0;    
+    gate->cur_block_sum = 0.0;
+
+    gate->is_active = false;
+    gate->cur_gain = 1.0;
+    gate->attack = 1.0 / 4096.0;
+    gate->release = 1.0 / 4096.0;
 }
 
 void gate_process(
@@ -53,7 +57,7 @@ void gate_process(
 	    last_frame = i;
 	}
     }
-
+    /*
     unsigned enable_pos = gate->is_active ? 0 : frame_size;
     unsigned disable_pos = gate->is_active ? frame_size : 0;
 
@@ -61,8 +65,8 @@ void gate_process(
 
     /* start from last block sum and iterate backwards to find
      * the timepoint where a block sum crosses the threshold
-     */
-    /*
+     * /
+
     unsigned i = gate->hist_idx;
     while( last_frame > 0 ) {
         if( gate->hist[ i ] < gate->threshold ) {
@@ -70,16 +74,23 @@ void gate_process(
         }
 
         last_frame -= gate->block_size;
-        i -= 1;
+        i = (i - 1) % gate->hist_size;
     }
-    */
-
+*/
 
     for( size_t i = 0; i < frame_size; ++i ) {
-      //        if( i >= enable_pos && i < disable_pos )
-        if( gate->is_active )
-            out[i] = in[i];
-	else
-	    out[i] = 0.0;
+        if( gate->is_active ) {
+            gate->cur_gain += gate->attack;
+            if( gate->cur_gain > 1.0 ) {
+                gate->cur_gain = 1.0;
+            }
+        } else {
+            gate->cur_gain -= gate->attack;
+            if( gate->cur_gain < 0.0 ) {
+                gate->cur_gain = 0.0;
+            }
+        }
+
+	out[i] = in[i] * gate->cur_gain;
     }  
 }
diff --git a/gate.h b/gate.h
index 9456ccd..c921d1b 100644
--- a/gate.h
+++ b/gate.h
@@ -18,6 +18,10 @@ struct gate {
   float cur_block_sum;
 
   bool is_active;
+  float cur_gain;
+
+  float attack;
+  float release;
 };
 
 void gate_init();

From e36a4635e8632b12819eac3d1f417c722c00c237 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Mon, 27 Jan 2025 23:16:59 +0100
Subject: [PATCH 07/12] simplify noise gate

---
 gate.c   | 70 +++++++++++++++-----------------------------------------
 gate.h   |  6 +----
 guitfx.c |  2 +-
 3 files changed, 21 insertions(+), 57 deletions(-)

diff --git a/gate.c b/gate.c
index a25a39c..bb11f25 100644
--- a/gate.c
+++ b/gate.c
@@ -12,15 +12,12 @@ void gate_init(
     gate->enable_calibration = false;
 
     gate->block_size = 1024;
-    gate->hist_size = 256;
-    gate->hist_idx = 0;
-    gate->hist = malloc( sizeof(float) * gate->hist_size );
     gate->cur_block_count = 0;
     gate->cur_block_sum = 0.0;
 
     gate->is_active = false;
     gate->cur_gain = 1.0;
-    gate->attack = 1.0 / 4096.0;
+    gate->attack = 1.0 / 512.0;
     gate->release = 1.0 / 4096.0;
 }
 
@@ -30,67 +27,38 @@ void gate_process(
     float const * in,
     float * out
 ) {
-    float sum = 0.0;
 
-
-    int last_frame = 0;
+  bool act = false;
 
     for( size_t i = 0; i < frame_size; ++i ) {
         gate->cur_block_sum += fabs( in[i] );
-        gate->cur_block_count += 1;
+	gate->cur_block_count += 1;
 
         if( gate->cur_block_count >= gate->block_size ) {
-	  printf("new block sum [%lu] (frame %lu) : %f\n", gate->hist_idx, i, gate->cur_block_sum);
+	  //printf("new block sum %f, gain = %f\n", gate->cur_block_sum, gate->cur_gain);
 
-	  if( gate->cur_block_sum > 20.0 ) {
-	    gate->is_active = true;
+	  gate->last_block_sum = gate->cur_block_sum;
+	  gate->cur_block_sum = 0.0;
+
+	  if( gate->last_block_sum > 0.03 ) {
+              act = true;
 	  } else {
-	    gate->is_active = false;
+              act = false;
 	  }
-
-            gate->hist[ gate->hist_idx ] = gate->cur_block_sum;
-	    gate->hist_idx = (gate->hist_idx + 1) % gate->hist_size;
-            gate->cur_block_sum = 0.0;
-	    gate->cur_block_count = 0;
-
-
-	    last_frame = i;
 	}
-    }
-    /*
-    unsigned enable_pos = gate->is_active ? 0 : frame_size;
-    unsigned disable_pos = gate->is_active ? frame_size : 0;
 
-    unsigned blocks_per_buffer = frame_size / gate->block_size;
-
-    /* start from last block sum and iterate backwards to find
-     * the timepoint where a block sum crosses the threshold
-     * /
-
-    unsigned i = gate->hist_idx;
-    while( last_frame > 0 ) {
-        if( gate->hist[ i ] < gate->threshold ) {
-            disable_pos = last_frame;
-        }
-
-        last_frame -= gate->block_size;
-        i = (i - 1) % gate->hist_size;
-    }
-*/
-
-    for( size_t i = 0; i < frame_size; ++i ) {
-        if( gate->is_active ) {
-            gate->cur_gain += gate->attack;
-            if( gate->cur_gain > 1.0 ) {
+	if( act ) {
+	    gate->cur_gain += gate->attack;
+	    if( gate->cur_gain > 1.0 ) {
                 gate->cur_gain = 1.0;
             }
-        } else {
-            gate->cur_gain -= gate->attack;
-            if( gate->cur_gain < 0.0 ) {
+	} else {
+	    gate->cur_gain -= gate->release;
+	    if( gate->cur_gain < 0.0 ) {
                 gate->cur_gain = 0.0;
             }
-        }
+	}
 
-	out[i] = in[i] * gate->cur_gain;
-    }  
+        out[i] = in[i] * gate->cur_gain;
+    }
 }
diff --git a/gate.h b/gate.h
index c921d1b..7f96d6a 100644
--- a/gate.h
+++ b/gate.h
@@ -9,13 +9,9 @@ struct gate {
   bool enable_calibration;
 
   unsigned block_size;
-
-  unsigned hist_idx;
-  unsigned hist_size;
-  float * hist;
-
   unsigned cur_block_count;
   float cur_block_sum;
+  float last_block_sum;
 
   bool is_active;
   float cur_gain;
diff --git a/guitfx.c b/guitfx.c
index 824310a..11ffe2c 100644
--- a/guitfx.c
+++ b/guitfx.c
@@ -107,7 +107,7 @@ static void on_process(void *userdata, struct spa_io_position *position)
 								      // noise gate calibration
 								      if( data->prog == 2 ) {
 									if( midi_data[2] >= 64) {
-									  data->gate.threshold = 0.0;//data->gate.cur_avg;
+									  data->gate.threshold = data->gate.cur_block_sum * 0.8;
 									  printf("calibrate noise gate: threshold = %f\n", data->gate.threshold); 
 									}
 								      }

From ce2212708a54cbe6c79bab3165301c4c35206c40 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Thu, 13 Feb 2025 18:00:59 +0100
Subject: [PATCH 08/12] up expr threshold

---
 guitfx.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/guitfx.c b/guitfx.c
index 11ffe2c..561156e 100644
--- a/guitfx.c
+++ b/guitfx.c
@@ -92,11 +92,12 @@ static void on_process(void *userdata, struct spa_io_position *position)
 									    // expr pedal
 							      		    float val_f = ((float)midi_data[2]) / 128.0;
 
-									    float thres = 0.4;
+									    float thres = 0.5;
 									    
 								            if ( val_f > thres ) {
-									        printf("Expr Pedal %f\n", val_f);
-										data->delay.mix = (val_f - thres) / (1.0-thres);
+   									        float expr_mix = (val_f - thres) / (1.0-thres);
+									        printf("Expr Pedal %f\n", expr_mix);
+										data->delay.mix = expr_mix;
 									    } else {
 									        data->delay.mix = 0.0;
 								            }

From 62acece20421a86bb769128a3bd8df2871e1ff65 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Mon, 17 Feb 2025 17:56:58 +0100
Subject: [PATCH 09/12] gate: decrease attack, increase release

---
 gate.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gate.c b/gate.c
index bb11f25..8829c9f 100644
--- a/gate.c
+++ b/gate.c
@@ -17,8 +17,8 @@ void gate_init(
 
     gate->is_active = false;
     gate->cur_gain = 1.0;
-    gate->attack = 1.0 / 512.0;
-    gate->release = 1.0 / 4096.0;
+    gate->attack = 1.0 / 128.0;
+    gate->release = 1.0 / 8192.0;
 }
 
 void gate_process(

From a6970aeed3b1f28b7f0dfc4ec2d2acec0a707675 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Mon, 17 Feb 2025 18:06:56 +0100
Subject: [PATCH 10/12] clang format

---
 delay.c  |  47 ++++---
 delay.h  |  19 ++-
 gate.c   |  62 ++++-----
 gate.h   |  33 +++--
 guitfx.c | 400 ++++++++++++++++++++++++++-----------------------------
 sust.c   |  78 ++++++-----
 sust.h   |  18 ++-
 7 files changed, 315 insertions(+), 342 deletions(-)

diff --git a/delay.c b/delay.c
index 31f3089..1723ca9 100644
--- a/delay.c
+++ b/delay.c
@@ -1,12 +1,12 @@
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
 #include "delay.h"
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
 
 void delay_init(
-    struct delay * delay
-) {
+    struct delay* delay)
+{
     delay->mix = 0.8;
     delay->feedback = 0.8;
     delay->duration = 0;
@@ -16,39 +16,38 @@ void delay_init(
 }
 
 void delay_set_time(
-    struct delay * delay,
-    uint64_t new_duration
-) {
+    struct delay* delay,
+    uint64_t new_duration)
+{
     printf("set delay duration to %lu samples\n", new_duration);
-    if( new_duration > delay->buf_capacity ) {
+    if (new_duration > delay->buf_capacity) {
         delay->buf_capacity = new_duration;
-        delay->buf = realloc( delay->buf, sizeof(float) * new_duration );
+        delay->buf = realloc(delay->buf, sizeof(float) * new_duration);
     }
 
-    for( int i = delay->duration; i < new_duration; ++i ) {
+    for (int i = delay->duration; i < new_duration; ++i) {
         delay->buf[i] = delay->buf[i - delay->duration];
     }
 
     delay->duration = new_duration;
-    if( new_duration > 0 ) {
+    if (new_duration > 0) {
         delay->buf_idx %= new_duration;
     }
 }
 
 void delay_process(
-    struct delay * delay,
+    struct delay* delay,
     size_t frame_size,
-    float const * in,
-    float * out
-) {
-    for( size_t i = 0; i < frame_size; ++i ) {
-        if( delay->duration > 0 ) {
-            out[i] =
-                in[i]
-                + delay->mix * delay->buf[ delay->buf_idx ];
+    float const* in,
+    float* out)
+{
+    for (size_t i = 0; i < frame_size; ++i) {
+        if (delay->duration > 0) {
+            out[i] = in[i]
+                + delay->mix * delay->buf[delay->buf_idx];
 
-            delay->buf[ delay->buf_idx ] *= delay->feedback;
-            delay->buf[ delay->buf_idx ] += (1.0 - delay->feedback) * in[i];
+            delay->buf[delay->buf_idx] *= delay->feedback;
+            delay->buf[delay->buf_idx] += (1.0 - delay->feedback) * in[i];
             delay->buf_idx = (delay->buf_idx + 1) % delay->duration;
         } else {
             out[i] = 0.5 * in[i];
diff --git a/delay.h b/delay.h
index 4ed2cf5..499b1cc 100644
--- a/delay.h
+++ b/delay.h
@@ -1,7 +1,7 @@
 #pragma once
 
-#include <stdint.h>
 #include <stddef.h>
+#include <stdint.h>
 
 struct delay {
     uint64_t duration;
@@ -10,21 +10,18 @@ struct delay {
 
     uint64_t buf_idx;
     size_t buf_capacity;
-    float * buf;
+    float* buf;
 };
 
 void delay_init(
-    struct delay * delay
-);
+    struct delay* delay);
 
 void delay_set_time(
-    struct delay * delay,
-    uint64_t new_duration
-);
+    struct delay* delay,
+    uint64_t new_duration);
 
 void delay_process(
-    struct delay * delay,
+    struct delay* delay,
     size_t frame_size,
-    float const * in,
-    float * out
-);
+    float const* in,
+    float* out);
diff --git a/gate.c b/gate.c
index 8829c9f..3f13c6a 100644
--- a/gate.c
+++ b/gate.c
@@ -1,13 +1,13 @@
-#include <stdint.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
 #include "gate.h"
 #include <math.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
 
 void gate_init(
-    struct gate * gate
-) {
+    struct gate* gate)
+{
     gate->threshold = 0.0;
     gate->enable_calibration = false;
 
@@ -22,42 +22,42 @@ void gate_init(
 }
 
 void gate_process(
-    struct gate * gate,
+    struct gate* gate,
     size_t frame_size,
-    float const * in,
-    float * out
-) {
+    float const* in,
+    float* out)
+{
 
-  bool act = false;
+    bool act = false;
 
-    for( size_t i = 0; i < frame_size; ++i ) {
-        gate->cur_block_sum += fabs( in[i] );
-	gate->cur_block_count += 1;
+    for (size_t i = 0; i < frame_size; ++i) {
+        gate->cur_block_sum += fabs(in[i]);
+        gate->cur_block_count += 1;
 
-        if( gate->cur_block_count >= gate->block_size ) {
-	  //printf("new block sum %f, gain = %f\n", gate->cur_block_sum, gate->cur_gain);
+        if (gate->cur_block_count >= gate->block_size) {
+            // printf("new block sum %f, gain = %f\n", gate->cur_block_sum, gate->cur_gain);
 
-	  gate->last_block_sum = gate->cur_block_sum;
-	  gate->cur_block_sum = 0.0;
+            gate->last_block_sum = gate->cur_block_sum;
+            gate->cur_block_sum = 0.0;
 
-	  if( gate->last_block_sum > 0.03 ) {
-              act = true;
-	  } else {
-              act = false;
-	  }
-	}
+            if (gate->last_block_sum > 0.03) {
+                act = true;
+            } else {
+                act = false;
+            }
+        }
 
-	if( act ) {
-	    gate->cur_gain += gate->attack;
-	    if( gate->cur_gain > 1.0 ) {
+        if (act) {
+            gate->cur_gain += gate->attack;
+            if (gate->cur_gain > 1.0) {
                 gate->cur_gain = 1.0;
             }
-	} else {
-	    gate->cur_gain -= gate->release;
-	    if( gate->cur_gain < 0.0 ) {
+        } else {
+            gate->cur_gain -= gate->release;
+            if (gate->cur_gain < 0.0) {
                 gate->cur_gain = 0.0;
             }
-	}
+        }
 
         out[i] = in[i] * gate->cur_gain;
     }
diff --git a/gate.h b/gate.h
index 7f96d6a..3773e5c 100644
--- a/gate.h
+++ b/gate.h
@@ -1,29 +1,28 @@
 #pragma once
 
-#include <stdint.h>
-#include <stddef.h>
 #include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
 
 struct gate {
-  float threshold;
-  bool enable_calibration;
+    float threshold;
+    bool enable_calibration;
 
-  unsigned block_size;
-  unsigned cur_block_count;
-  float cur_block_sum;
-  float last_block_sum;
+    unsigned block_size;
+    unsigned cur_block_count;
+    float cur_block_sum;
+    float last_block_sum;
 
-  bool is_active;
-  float cur_gain;
+    bool is_active;
+    float cur_gain;
 
-  float attack;
-  float release;
+    float attack;
+    float release;
 };
 
 void gate_init();
 void gate_process(
-     struct gate * gate,
-     size_t frame_size,
-     float const * in,
-     float * out
-);
+    struct gate* gate,
+    size_t frame_size,
+    float const* in,
+    float* out);
diff --git a/guitfx.c b/guitfx.c
index 561156e..90d2f4f 100644
--- a/guitfx.c
+++ b/guitfx.c
@@ -2,283 +2,259 @@
 #include "pipewire/port.h"
 #include "spa/pod/iter.h"
 #include "spa/utils/defs.h"
-#include <stdio.h>
 #include <signal.h>
+#include <stdio.h>
 
-#include <spa/pod/pod.h>
-#include <spa/pod/builder.h>
 #include <spa/control/control.h>
 #include <spa/param/latency-utils.h>
+#include <spa/pod/builder.h>
+#include <spa/pod/pod.h>
 
-#include <pipewire/pipewire.h>
 #include <pipewire/filter.h>
+#include <pipewire/pipewire.h>
 
 #include "delay.h"
-#include "sust.h"
 #include "gate.h"
+#include "sust.h"
 
-float envelope( float x );
+float envelope(float x);
 
 struct data;
 
 struct port {
-	struct data *data;
+    struct data* data;
 };
 
 struct data {
-	struct pw_main_loop * loop;
-	struct pw_filter * filter;
-	struct port * guit_in_port;
-	struct port * midi_in_port;
-	struct port * out_port;
+    struct pw_main_loop* loop;
+    struct pw_filter* filter;
+    struct port* guit_in_port;
+    struct port* midi_in_port;
+    struct port* out_port;
 
-	//! effect data
-	uint64_t last_tap;
-	struct delay delay;
-	struct sust sust;
-        struct gate gate;
+    //! effect data
+    uint64_t last_tap;
+    struct delay delay;
+    struct sust sust;
+    struct gate gate;
 
-        unsigned prog;
+    unsigned prog;
 
-	//! elapsed time in number of samples
-	uint64_t time;
+    //! elapsed time in number of samples
+    uint64_t time;
 };
 
-static void on_process(void *userdata, struct spa_io_position *position)
+static void on_process(void* userdata, struct spa_io_position* position)
 {
-	struct data *data = userdata;
+    struct data* data = userdata;
 
-	uint32_t n_samples = position->clock.duration;
-	uint64_t frame = data->time;
-	data->time += n_samples;
+    uint32_t n_samples = position->clock.duration;
+    uint64_t frame = data->time;
+    data->time += n_samples;
 
-	struct pw_buffer * b = pw_filter_dequeue_buffer(data->midi_in_port);
-	if( b == NULL ) {
-	   fprintf(stderr, "on_process(): no buffer for midi_in_port\n");
-	   return;
-	}
+    struct pw_buffer* b = pw_filter_dequeue_buffer(data->midi_in_port);
+    if (b == NULL) {
+        fprintf(stderr, "on_process(): no buffer for midi_in_port\n");
+        return;
+    }
 
-	struct spa_buffer * buf = b->buffer;
-	spa_assert(buf->n_datas == 1);
-	struct spa_data   * d   = &buf->datas[0];
+    struct spa_buffer* buf = b->buffer;
+    spa_assert(buf->n_datas == 1);
+    struct spa_data* d = &buf->datas[0];
 
-	if( d->data ) {
-	    struct spa_pod * pod =
-			spa_pod_from_data(
-			    d->data,
-				d->maxsize,
-				d->chunk->offset,
-				d->chunk->size
-			);
+    if (d->data) {
+        struct spa_pod* pod = spa_pod_from_data(d->data, d->maxsize,
+            d->chunk->offset, d->chunk->size);
 
-		if( pod ) {
-		    if( spa_pod_is_sequence(pod) ) {
-				struct spa_pod_sequence * pod_seq = (struct spa_pod_sequence*) pod;
-				struct spa_pod_control * c;
-				SPA_POD_SEQUENCE_FOREACH(pod_seq, c) {
-				    if( c->type == SPA_CONTROL_Midi ) {
-						unsigned sec =
-						      (frame + c->offset)
-							/ (float) position->clock.rate.denom;
-						char * midi_data = SPA_POD_BODY(&c->value);
-						unsigned size = SPA_POD_BODY_SIZE(&c->value);
+        if (pod) {
+            if (spa_pod_is_sequence(pod)) {
+                struct spa_pod_sequence* pod_seq = (struct spa_pod_sequence*)pod;
+                struct spa_pod_control* c;
+                SPA_POD_SEQUENCE_FOREACH(pod_seq, c)
+                {
+                    if (c->type == SPA_CONTROL_Midi) {
+                        unsigned sec = (frame + c->offset) / (float)position->clock.rate.denom;
+                        char* midi_data = SPA_POD_BODY(&c->value);
+                        unsigned size = SPA_POD_BODY_SIZE(&c->value);
 
-						printf("[%d] MIDI message (%d bytes) : %x, %x, %x\n", sec, size, midi_data[0], midi_data[1], midi_data[2]);
+                        printf("[%d] MIDI message (%d bytes) : %x, %x, %x\n", sec, size,
+                            midi_data[0], midi_data[1], midi_data[2]);
 
-						switch( midi_data[0] & 0xff ) {
-						    case 0xb0:
-								switch( midi_data[1] ) {
-									case 0x0b:
-									    // expr pedal
-							      		    float val_f = ((float)midi_data[2]) / 128.0;
+                        switch (midi_data[0] & 0xff) {
+                        case 0xb0:
+                            switch (midi_data[1]) {
+                            case 0x0b:
+                                // expr pedal
+                                float val_f = ((float)midi_data[2]) / 128.0;
 
-									    float thres = 0.5;
-									    
-								            if ( val_f > thres ) {
-   									        float expr_mix = (val_f - thres) / (1.0-thres);
-									        printf("Expr Pedal %f\n", expr_mix);
-										data->delay.mix = expr_mix;
-									    } else {
-									        data->delay.mix = 0.0;
-								            }
-									    break;
+                                float thres = 0.5;
 
-								    case 0x42:
+                                if (val_f > thres) {
+                                    float expr_mix = (val_f - thres) / (1.0 - thres);
+                                    printf("Expr Pedal %f\n", expr_mix);
+                                    data->delay.mix = expr_mix;
+                                } else {
+                                    data->delay.mix = 0.0;
+                                }
+                                break;
 
-								      // noise gate calibration
-								      if( data->prog == 2 ) {
-									if( midi_data[2] >= 64) {
-									  data->gate.threshold = data->gate.cur_block_sum * 0.8;
-									  printf("calibrate noise gate: threshold = %f\n", data->gate.threshold); 
-									}
-								      }
+                            case 0x42:
 
-							  	      // sust pedal
-								      if( data->prog == 1 ) {
-										if( midi_data[2] >= 64) {
-											sust_swap( &data->sust );
-										    data->sust.playing = true;
+                                // noise gate calibration
+                                if (data->prog == 2) {
+                                    if (midi_data[2] >= 64) {
+                                        data->gate.threshold = data->gate.cur_block_sum * 0.8;
+                                        printf("calibrate noise gate: threshold = %f\n",
+                                            data->gate.threshold);
+                                    }
+                                }
 
-											data->sust.start_idx = data->sust.idx;
-											data->sust.idx = 0;
-										} else {
-											data->sust.playing = false;
-										}
-								      }
+                                // sust pedal
+                                if (data->prog == 1) {
+                                    if (midi_data[2] >= 64) {
+                                        sust_swap(&data->sust);
+                                        data->sust.playing = true;
 
-										// tap tempo
-										if( data->prog == 0 ) {
-										  uint64_t cur_tap = frame + c->offset;
-										  uint64_t duration = cur_tap - data->last_tap;
-										  data->last_tap = cur_tap;
-										  if( duration < (4*position->clock.rate.denom) ) {
-										    delay_set_time( &data->delay, duration );
-										  }
+                                        data->sust.start_idx = data->sust.idx;
+                                        data->sust.idx = 0;
+                                    } else {
+                                        data->sust.playing = false;
+                                    }
+                                }
 
+                                // tap tempo
+                                if (data->prog == 0) {
+                                    uint64_t cur_tap = frame + c->offset;
+                                    uint64_t duration = cur_tap - data->last_tap;
+                                    data->last_tap = cur_tap;
+                                    if (duration < (4 * position->clock.rate.denom)) {
+                                        delay_set_time(&data->delay, duration);
+                                    }
 
-										  sust_resize( &data->sust, duration );
-										}
+                                    sust_resize(&data->sust, duration);
+                                }
 
-										break;
-								}
+                                break;
+                            }
 
-								break;
+                            break;
 
-							case 0xc0:
-							    // program change
-										if( midi_data[2] >= 64) {
-											sust_swap( &data->sust );
-										    data->sust.playing = true;
+                        case 0xc0:
+                            // program change
+                            if (midi_data[2] >= 64) {
+                                sust_swap(&data->sust);
+                                data->sust.playing = true;
 
-											data->sust.start_idx = data->sust.idx;
-											data->sust.idx = 0;
-										} else {
-											data->sust.playing = false;
-										}
-								printf("program change: %u\n", midi_data[1]);
-								data->prog = midi_data[1];
-							    break;
-						}
-					} else {
-					    printf("on_process(): non midi-control\n");
-					}
-				}
-			} else {
-			    fprintf(stderr, "on_process(): unexpected POD that is not a sequence (midi_in_port)\n");
-			}
-		} else {
-		    fprintf(stderr, "on_process(): pod is NULL\n");
-		}
-	} else {
-	    fprintf(stderr, "on_process(): no data in buffer of midi_in_port\n");
-	}
+                                data->sust.start_idx = data->sust.idx;
+                                data->sust.idx = 0;
+                            } else {
+                                data->sust.playing = false;
+                            }
+                            printf("program change: %u\n", midi_data[1]);
+                            data->prog = midi_data[1];
+                            break;
+                        }
+                    } else {
+                        printf("on_process(): non midi-control\n");
+                    }
+                }
+            } else {
+                fprintf(stderr, "on_process(): unexpected POD that is not a sequence "
+                                "(midi_in_port)\n");
+            }
+        } else {
+            fprintf(stderr, "on_process(): pod is NULL\n");
+        }
+    } else {
+        fprintf(stderr, "on_process(): no data in buffer of midi_in_port\n");
+    }
 
-	pw_filter_queue_buffer(data->midi_in_port, b);
+    pw_filter_queue_buffer(data->midi_in_port, b);
 
-
-	float * const in = pw_filter_get_dsp_buffer(data->guit_in_port, n_samples);
-	float tmp1[n_samples];
-	float tmp2[n_samples];
-	float * out = pw_filter_get_dsp_buffer(data->out_port, n_samples);
-	if( in && out ) {
-	  gate_process( &data->gate, n_samples, in, tmp1 );
-	  sust_process( &data->sust, n_samples, tmp1, tmp2 );
-	  delay_process( &data->delay, n_samples, tmp2, out );
-	}
+    float* const in = pw_filter_get_dsp_buffer(data->guit_in_port, n_samples);
+    float tmp1[n_samples];
+    float tmp2[n_samples];
+    float* out = pw_filter_get_dsp_buffer(data->out_port, n_samples);
+    if (in && out) {
+        gate_process(&data->gate, n_samples, in, tmp1);
+        sust_process(&data->sust, n_samples, tmp1, tmp2);
+        delay_process(&data->delay, n_samples, tmp2, out);
+    }
 }
 
 static const struct pw_filter_events filter_events = {
-	PW_VERSION_FILTER_EVENTS,
-	.process = on_process,
+    PW_VERSION_FILTER_EVENTS,
+    .process = on_process,
 };
 
-static void do_quit(void *userdata, int signal_number)
+static void do_quit(void* userdata, int signal_number)
 {
-	struct data *data = userdata;
-	pw_main_loop_quit(data->loop);
+    struct data* data = userdata;
+    pw_main_loop_quit(data->loop);
 }
 
-int main(int argc, char *argv[])
+int main(int argc, char* argv[])
 {
-	struct data data = { 0, };
+    struct data data = {
+        0,
+    };
 
-	gate_init( &data.gate );
-	delay_init( &data.delay );
-	sust_init( &data.sust );
-	data.prog = 0;
+    gate_init(&data.gate);
+    delay_init(&data.delay);
+    sust_init(&data.sust);
+    data.prog = 0;
 
-	const struct spa_pod *params[1];
-	uint8_t buffer[1024];
-	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
+    const struct spa_pod* params[1];
+    uint8_t buffer[1024];
+    struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
 
-	pw_init(&argc, &argv);
+    pw_init(&argc, &argv);
 
-	data.loop = pw_main_loop_new(NULL);
+    data.loop = pw_main_loop_new(NULL);
 
-	pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGINT, do_quit, &data);
-	pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGTERM, do_quit, &data);
+    pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGINT, do_quit, &data);
+    pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGTERM, do_quit, &data);
 
-	data.filter = pw_filter_new_simple(
-			pw_main_loop_get_loop(data.loop),
-			"Guitar FX",
-			pw_properties_new(
-				PW_KEY_MEDIA_TYPE, "Audio",
-				PW_KEY_MEDIA_CATEGORY, "Filter",
-				PW_KEY_MEDIA_ROLE, "DSP",
-				NULL),
-			&filter_events,
-			&data);
+    data.filter = pw_filter_new_simple(
+        pw_main_loop_get_loop(data.loop), "Guitar FX",
+        pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY,
+            "Filter", PW_KEY_MEDIA_ROLE, "DSP", NULL),
+        &filter_events, &data);
 
-	data.midi_in_port = pw_filter_add_port(data.filter,
-		    PW_DIRECTION_INPUT,
-			PW_FILTER_PORT_FLAG_MAP_BUFFERS,
-			sizeof(struct port),
-			pw_properties_new(
-			    PW_KEY_FORMAT_DSP, "8 bit raw midi",
-				PW_KEY_PORT_NAME, "midi in",
-				NULL
-			),
-			NULL, 0
-		);
+    data.midi_in_port = pw_filter_add_port(data.filter, PW_DIRECTION_INPUT,
+        PW_FILTER_PORT_FLAG_MAP_BUFFERS, sizeof(struct port),
+        pw_properties_new(PW_KEY_FORMAT_DSP, "8 bit raw midi",
+            PW_KEY_PORT_NAME, "midi in", NULL),
+        NULL, 0);
 
-	data.guit_in_port = pw_filter_add_port(data.filter,
-			PW_DIRECTION_INPUT,
-			PW_FILTER_PORT_FLAG_MAP_BUFFERS,
-			sizeof(struct port),
-			pw_properties_new(
-				PW_KEY_FORMAT_DSP, "32 bit float mono audio",
-				PW_KEY_PORT_NAME, "guitar in",
-				NULL),
-			NULL, 0);
+    data.guit_in_port = pw_filter_add_port(
+        data.filter, PW_DIRECTION_INPUT, PW_FILTER_PORT_FLAG_MAP_BUFFERS,
+        sizeof(struct port),
+        pw_properties_new(PW_KEY_FORMAT_DSP, "32 bit float mono audio",
+            PW_KEY_PORT_NAME, "guitar in", NULL),
+        NULL, 0);
 
-	data.out_port = pw_filter_add_port(data.filter,
-			PW_DIRECTION_OUTPUT,
-			PW_FILTER_PORT_FLAG_MAP_BUFFERS,
-			sizeof(struct port),
-			pw_properties_new(
-				PW_KEY_FORMAT_DSP, "32 bit float mono audio",
-				PW_KEY_PORT_NAME, "fx out",
-				NULL),
-			NULL, 0);
+    data.out_port = pw_filter_add_port(
+        data.filter, PW_DIRECTION_OUTPUT, PW_FILTER_PORT_FLAG_MAP_BUFFERS,
+        sizeof(struct port),
+        pw_properties_new(PW_KEY_FORMAT_DSP, "32 bit float mono audio",
+            PW_KEY_PORT_NAME, "fx out", NULL),
+        NULL, 0);
 
-	params[0] = spa_process_latency_build(&b,
-			SPA_PARAM_ProcessLatency,
-			&SPA_PROCESS_LATENCY_INFO_INIT(
-				.ns = 10 * SPA_NSEC_PER_MSEC
-			));
+    params[0] = spa_process_latency_build(
+        &b, SPA_PARAM_ProcessLatency,
+        &SPA_PROCESS_LATENCY_INFO_INIT(.ns = 10 * SPA_NSEC_PER_MSEC));
 
-	if (pw_filter_connect(data.filter,
-				PW_FILTER_FLAG_RT_PROCESS,
-				params, 1) < 0) {
-		fprintf(stderr, "can't connect\n");
-		return -1;
-	}
+    if (pw_filter_connect(data.filter, PW_FILTER_FLAG_RT_PROCESS, params, 1) < 0) {
+        fprintf(stderr, "can't connect\n");
+        return -1;
+    }
 
-	pw_main_loop_run(data.loop);
+    pw_main_loop_run(data.loop);
 
-	pw_filter_destroy(data.filter);
-	pw_main_loop_destroy(data.loop);
-	pw_deinit();
+    pw_filter_destroy(data.filter);
+    pw_main_loop_destroy(data.loop);
+    pw_deinit();
 
-	return 0;
+    return 0;
 }
diff --git a/sust.c b/sust.c
index d684930..f9d31a2 100644
--- a/sust.c
+++ b/sust.c
@@ -1,9 +1,10 @@
 #include "sust.h"
 #include <math.h>
-#include <stdlib.h>
 #include <stdio.h>
+#include <stdlib.h>
 
-void sust_init( struct sust * sust ) {
+void sust_init(struct sust* sust)
+{
     sust->mode = MODE_Sostenuto;
 
     sust->playing = false;
@@ -12,73 +13,76 @@ void sust_init( struct sust * sust ) {
     sust->idx = 0;
 
     sust->buf_len = 51200;
-    sust->record_buf = malloc( sizeof(float) * sust->buf_len );
-    sust->play_buf = malloc( sizeof(float) * sust->buf_len );
+    sust->record_buf = malloc(sizeof(float) * sust->buf_len);
+    sust->play_buf = malloc(sizeof(float) * sust->buf_len);
 }
 
-float envelope( float x ) {
-    if( x < 0.4 )
+float envelope(float x)
+{
+    if (x < 0.4)
         return (x / 0.4) * (x / 0.4);
-    if( x < 0.6 )
+    if (x < 0.6)
         return 1.0;
 
-   float v  = 1.0 - ((x - 0.6) / 0.4);
-   return v*v;
+    float v = 1.0 - ((x - 0.6) / 0.4);
+    return v * v;
 }
 
-float softcos( float x ) {
-    return sin( x * (3.141 / 2.0) );
+float softcos(float x)
+{
+    return sin(x * (3.141 / 2.0));
 }
 
-void sust_resize( struct sust * sust, size_t new_len ) {
-  sust->buf_len = new_len;
-  sust->play_buf = realloc( sust->play_buf, sizeof(float) * new_len );
-  sust->record_buf = realloc( sust->record_buf, sizeof(float) * new_len );
-  sust->idx %= new_len;
+void sust_resize(struct sust* sust, size_t new_len)
+{
+    sust->buf_len = new_len;
+    sust->play_buf = realloc(sust->play_buf, sizeof(float) * new_len);
+    sust->record_buf = realloc(sust->record_buf, sizeof(float) * new_len);
+    sust->idx %= new_len;
 }
 
 void sust_swap(
-    struct sust * sust
-) {
-    float * tmp = sust->play_buf;
+    struct sust* sust)
+{
+    float* tmp = sust->play_buf;
     sust->play_buf = sust->record_buf;
     sust->record_buf = tmp;
 
-    for( int i = 0; i  < sust->buf_len; ++i ) {
+    for (int i = 0; i < sust->buf_len; ++i) {
         sust->record_buf[i] = sust->play_buf[i];
     }
 }
 
 void sust_process(
-    struct sust * sust,
+    struct sust* sust,
     size_t frame_size,
-    float const * in,
-    float * out
-) {
-    for( size_t i = 0; i < frame_size; ++i ) {
+    float const* in,
+    float* out)
+{
+    for (size_t i = 0; i < frame_size; ++i) {
 
-        if( sust->mode == MODE_Sustain && sust->idx == 0 ) {
-            sust_swap( sust );
+        if (sust->mode == MODE_Sustain && sust->idx == 0) {
+            sust_swap(sust);
         }
 
         float out_value = in[i];
 
-        if( sust->playing ) {
+        if (sust->playing) {
             int n_voices = 5;
 
-            for( int v = 0; v < n_voices; ++v ) {
-                size_t play_idx = (sust->start_idx + sust->idx + ((v*sust->buf_len)/n_voices)) % sust->buf_len;
-                float gain = envelope( ((float) play_idx) / ((float)sust->buf_len) );
-               //printf("gain = %f\n", gain);
-                out_value += 0.5 *gain * sust->play_buf[ play_idx ];
+            for (int v = 0; v < n_voices; ++v) {
+                size_t play_idx = (sust->start_idx + sust->idx + ((v * sust->buf_len) / n_voices)) % sust->buf_len;
+                float gain = envelope(((float)play_idx) / ((float)sust->buf_len));
+                // printf("gain = %f\n", gain);
+                out_value += 0.5 * gain * sust->play_buf[play_idx];
             }
         }
 
-        if( sust->mode == MODE_Sostenuto ) {
-            sust->record_buf[ sust->idx ] = in[i];
+        if (sust->mode == MODE_Sostenuto) {
+            sust->record_buf[sust->idx] = in[i];
         }
-        if( sust->mode == MODE_Sustain ) {
-            sust->record_buf[ sust->idx ] = 0.5*out_value;
+        if (sust->mode == MODE_Sustain) {
+            sust->record_buf[sust->idx] = 0.5 * out_value;
         }
 
         sust->idx = (sust->idx + 1) % sust->buf_len;
diff --git a/sust.h b/sust.h
index 92bece8..a0472b1 100644
--- a/sust.h
+++ b/sust.h
@@ -15,18 +15,16 @@ struct sust {
     size_t start_idx;
     size_t idx;
     size_t buf_len;
-    float * record_buf;
-    float * play_buf;
+    float* record_buf;
+    float* play_buf;
 };
 
-
-void sust_init( struct sust * sust );
-void sust_resize( struct sust * sust, size_t new_len );
-void sust_swap( struct sust * sust );
+void sust_init(struct sust* sust);
+void sust_resize(struct sust* sust, size_t new_len);
+void sust_swap(struct sust* sust);
 
 void sust_process(
-    struct sust * sust,
+    struct sust* sust,
     size_t frame_size,
-    float const * in,
-    float * out
-);
+    float const* in,
+    float* out);

From 752155de328e1407bc486dd9bc5f83f577276160 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Mon, 17 Feb 2025 18:07:53 +0100
Subject: [PATCH 11/12] add gitignore

---
 .gitignore | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 .gitignore

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dee53c5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+build
+*~

From 765275ac325e46552700e051615982b936da8a78 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Mon, 17 Feb 2025 18:59:37 +0100
Subject: [PATCH 12/12] move midi-control into separate function&file

---
 controller.c | 110 +++++++++++++++++++++++++++++++++++++++++++++
 guitfx.c     | 125 +++++++++++----------------------------------------
 guitfx.h     |  16 +++++++
 meson.build  |   1 +
 4 files changed, 153 insertions(+), 99 deletions(-)
 create mode 100644 controller.c
 create mode 100644 guitfx.h

diff --git a/controller.c b/controller.c
new file mode 100644
index 0000000..76e38b7
--- /dev/null
+++ b/controller.c
@@ -0,0 +1,110 @@
+#include "pipewire/keys.h"
+#include "pipewire/port.h"
+#include "spa/pod/iter.h"
+#include "spa/utils/defs.h"
+#include <signal.h>
+#include <stdio.h>
+
+#include <spa/control/control.h>
+#include <spa/param/latency-utils.h>
+#include <spa/pod/builder.h>
+#include <spa/pod/pod.h>
+
+#include <pipewire/filter.h>
+#include <pipewire/pipewire.h>
+
+#include "guitfx.h"
+#include "delay.h"
+#include "sust.h"
+#include "gate.h"
+
+void midi_control(
+		  struct FxData * data,
+
+		  uint64_t frame,
+		  uint64_t offset,
+		  unsigned sec,
+		  unsigned midi_size,
+		  uint8_t * midi_data,
+		  struct spa_io_position* position
+) {
+  printf("[%d] MIDI message (%d bytes) : %x, %x, %x\n", sec, midi_size,
+	 midi_data[0], midi_data[1], midi_data[2]);
+
+                        switch (midi_data[0] & 0xff) {
+                        case 0xb0:
+                            switch (midi_data[1]) {
+                            case 0x0b:
+                                // expr pedal
+                                float val_f = ((float)midi_data[2]) / 128.0;
+
+                                float thres = 0.5;
+
+                                if (val_f > thres) {
+                                    float expr_mix = (val_f - thres) / (1.0 - thres);
+                                    printf("Expr Pedal %f\n", expr_mix);
+
+                                    data->delay.mix = expr_mix;
+                                } else {
+                                    data->delay.mix = 0.0;
+                                }
+                                break;
+
+                            case 0x42:
+
+                                // noise gate calibration
+                                if (data->prog == 2) {
+                                    if (midi_data[2] >= 64) {
+                                        data->gate.threshold = data->gate.cur_block_sum * 0.8;
+                                        printf("calibrate noise gate: threshold = %f\n",
+                                            data->gate.threshold);
+                                    }
+                                }
+
+                                // sust pedal
+                                if (data->prog == 1) {
+                                    if (midi_data[2] >= 64) {
+                                        sust_swap(&data->sust);
+                                        data->sust.playing = true;
+
+                                        data->sust.start_idx = data->sust.idx;
+                                        data->sust.idx = 0;
+                                    } else {
+                                        data->sust.playing = false;
+                                    }
+                                }
+
+                                // tap tempo
+                                if (data->prog == 0) {
+                                    uint64_t cur_tap = frame + offset;
+                                    uint64_t duration = cur_tap - data->last_tap;
+                                    data->last_tap = cur_tap;
+                                    if (duration < (4 * position->clock.rate.denom)) {
+                                        delay_set_time(&data->delay, duration);
+                                    }
+
+                                    sust_resize(&data->sust, duration);
+                                }
+
+                                break;
+                            }
+
+                            break;
+
+                        case 0xc0:
+                            // program change
+                            if (midi_data[2] >= 64) {
+                                sust_swap(&data->sust);
+                                data->sust.playing = true;
+
+                                data->sust.start_idx = data->sust.idx;
+                                data->sust.idx = 0;
+                            } else {
+                                data->sust.playing = false;
+                            }
+                            printf("program change: %u\n", midi_data[1]);
+                            data->prog = midi_data[1];
+                            break;
+                        }
+}
+
diff --git a/guitfx.c b/guitfx.c
index 90d2f4f..82b53ca 100644
--- a/guitfx.c
+++ b/guitfx.c
@@ -16,6 +16,7 @@
 #include "delay.h"
 #include "gate.h"
 #include "sust.h"
+#include "guitfx.h"
 
 float envelope(float x);
 
@@ -25,23 +26,24 @@ struct port {
     struct data* data;
 };
 
+void midi_control(
+		  struct FxData * data,
+
+		  uint64_t frame,
+		  uint64_t offset,
+		  unsigned sec,
+		  unsigned midi_size,
+		  uint8_t * midi_data,
+		  struct spa_io_position* position);
+
+
 struct data {
     struct pw_main_loop* loop;
     struct pw_filter* filter;
     struct port* guit_in_port;
     struct port* midi_in_port;
     struct port* out_port;
-
-    //! effect data
-    uint64_t last_tap;
-    struct delay delay;
-    struct sust sust;
-    struct gate gate;
-
-    unsigned prog;
-
-    //! elapsed time in number of samples
-    uint64_t time;
+    struct FxData fx_data;
 };
 
 static void on_process(void* userdata, struct spa_io_position* position)
@@ -49,8 +51,8 @@ static void on_process(void* userdata, struct spa_io_position* position)
     struct data* data = userdata;
 
     uint32_t n_samples = position->clock.duration;
-    uint64_t frame = data->time;
-    data->time += n_samples;
+    uint64_t frame = data->fx_data.time;
+    data->fx_data.time += n_samples;
 
     struct pw_buffer* b = pw_filter_dequeue_buffer(data->midi_in_port);
     if (b == NULL) {
@@ -75,87 +77,12 @@ static void on_process(void* userdata, struct spa_io_position* position)
                     if (c->type == SPA_CONTROL_Midi) {
                         unsigned sec = (frame + c->offset) / (float)position->clock.rate.denom;
                         char* midi_data = SPA_POD_BODY(&c->value);
-                        unsigned size = SPA_POD_BODY_SIZE(&c->value);
+                        unsigned midi_size = SPA_POD_BODY_SIZE(&c->value);
 
-                        printf("[%d] MIDI message (%d bytes) : %x, %x, %x\n", sec, size,
-                            midi_data[0], midi_data[1], midi_data[2]);
-
-                        switch (midi_data[0] & 0xff) {
-                        case 0xb0:
-                            switch (midi_data[1]) {
-                            case 0x0b:
-                                // expr pedal
-                                float val_f = ((float)midi_data[2]) / 128.0;
-
-                                float thres = 0.5;
-
-                                if (val_f > thres) {
-                                    float expr_mix = (val_f - thres) / (1.0 - thres);
-                                    printf("Expr Pedal %f\n", expr_mix);
-                                    data->delay.mix = expr_mix;
-                                } else {
-                                    data->delay.mix = 0.0;
-                                }
-                                break;
-
-                            case 0x42:
-
-                                // noise gate calibration
-                                if (data->prog == 2) {
-                                    if (midi_data[2] >= 64) {
-                                        data->gate.threshold = data->gate.cur_block_sum * 0.8;
-                                        printf("calibrate noise gate: threshold = %f\n",
-                                            data->gate.threshold);
-                                    }
-                                }
-
-                                // sust pedal
-                                if (data->prog == 1) {
-                                    if (midi_data[2] >= 64) {
-                                        sust_swap(&data->sust);
-                                        data->sust.playing = true;
-
-                                        data->sust.start_idx = data->sust.idx;
-                                        data->sust.idx = 0;
-                                    } else {
-                                        data->sust.playing = false;
-                                    }
-                                }
-
-                                // tap tempo
-                                if (data->prog == 0) {
-                                    uint64_t cur_tap = frame + c->offset;
-                                    uint64_t duration = cur_tap - data->last_tap;
-                                    data->last_tap = cur_tap;
-                                    if (duration < (4 * position->clock.rate.denom)) {
-                                        delay_set_time(&data->delay, duration);
-                                    }
-
-                                    sust_resize(&data->sust, duration);
-                                }
-
-                                break;
-                            }
-
-                            break;
-
-                        case 0xc0:
-                            // program change
-                            if (midi_data[2] >= 64) {
-                                sust_swap(&data->sust);
-                                data->sust.playing = true;
-
-                                data->sust.start_idx = data->sust.idx;
-                                data->sust.idx = 0;
-                            } else {
-                                data->sust.playing = false;
-                            }
-                            printf("program change: %u\n", midi_data[1]);
-                            data->prog = midi_data[1];
-                            break;
-                        }
+			midi_control( &data->fx_data, frame, c->offset, sec, midi_size, midi_data, position );
                     } else {
-                        printf("on_process(): non midi-control\n");
+
+		      printf("on_process(): non midi-control\n");
                     }
                 }
             } else {
@@ -176,9 +103,9 @@ static void on_process(void* userdata, struct spa_io_position* position)
     float tmp2[n_samples];
     float* out = pw_filter_get_dsp_buffer(data->out_port, n_samples);
     if (in && out) {
-        gate_process(&data->gate, n_samples, in, tmp1);
-        sust_process(&data->sust, n_samples, tmp1, tmp2);
-        delay_process(&data->delay, n_samples, tmp2, out);
+        gate_process(&data->fx_data.gate, n_samples, in, tmp1);
+        sust_process(&data->fx_data.sust, n_samples, tmp1, tmp2);
+        delay_process(&data->fx_data.delay, n_samples, tmp2, out);
     }
 }
 
@@ -199,10 +126,10 @@ int main(int argc, char* argv[])
         0,
     };
 
-    gate_init(&data.gate);
-    delay_init(&data.delay);
-    sust_init(&data.sust);
-    data.prog = 0;
+    gate_init(&data.fx_data.gate);
+    delay_init(&data.fx_data.delay);
+    sust_init(&data.fx_data.sust);
+    data.fx_data.prog = 0;
 
     const struct spa_pod* params[1];
     uint8_t buffer[1024];
diff --git a/guitfx.h b/guitfx.h
new file mode 100644
index 0000000..4dedcdd
--- /dev/null
+++ b/guitfx.h
@@ -0,0 +1,16 @@
+
+#include "delay.h"
+#include "sust.h"
+#include "gate.h"
+
+struct FxData {
+    uint64_t last_tap;
+    struct delay delay;
+    struct sust sust;
+    struct gate gate;
+
+    unsigned prog;
+
+    //! elapsed time in number of samples
+    uint64_t time;
+};
diff --git a/meson.build b/meson.build
index d59ef2b..ab53a04 100644
--- a/meson.build
+++ b/meson.build
@@ -10,5 +10,6 @@ executable(
     'delay.c',
     'sust.c',
     'gate.c',
+    'controller.c',
     dependencies : [pipewire_dep, m_dep],
 )