Compare commits
5 commits
dev
...
topic-nois
Author | SHA1 | Date | |
---|---|---|---|
db9d2d4e9f | |||
bcead0e0b5 | |||
c75d61123a | |||
52f09d7728 | |||
3a4a02a138 |
7 changed files with 271 additions and 14 deletions
4
delay.c
4
delay.c
|
@ -44,8 +44,8 @@ void delay_process(
|
||||||
for( size_t i = 0; i < frame_size; ++i ) {
|
for( size_t i = 0; i < frame_size; ++i ) {
|
||||||
if( delay->duration > 0 ) {
|
if( delay->duration > 0 ) {
|
||||||
out[i] =
|
out[i] =
|
||||||
0.5 * in[i]
|
in[i]
|
||||||
+ 0.5 * delay->mix * delay->buf[ delay->buf_idx ];
|
+ delay->mix * delay->buf[ delay->buf_idx ];
|
||||||
|
|
||||||
delay->buf[ delay->buf_idx ] *= delay->feedback;
|
delay->buf[ delay->buf_idx ] *= delay->feedback;
|
||||||
delay->buf[ delay->buf_idx ] += (1.0 - delay->feedback) * in[i];
|
delay->buf[ delay->buf_idx ] += (1.0 - delay->feedback) * in[i];
|
||||||
|
|
53
gate.c
Normal file
53
gate.c
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#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->cur_state = false;
|
||||||
|
gate->cur_avg = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gate_process(
|
||||||
|
struct gate * gate,
|
||||||
|
size_t frame_size,
|
||||||
|
float const * in,
|
||||||
|
float * out
|
||||||
|
) {
|
||||||
|
float sum = 0.0;
|
||||||
|
|
||||||
|
for( size_t i = 0; i < frame_size; ++i ) {
|
||||||
|
sum += fabs( in[i] );
|
||||||
|
}
|
||||||
|
|
||||||
|
gate->cur_avg = sum / frame_size;
|
||||||
|
|
||||||
|
if( gate->cur_avg > gate->threshold ) {
|
||||||
|
|
||||||
|
if( ! gate->cur_state ) {
|
||||||
|
gate->cur_state = true;
|
||||||
|
printf("GATE on\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for( size_t i = 0; i < frame_size; ++i ) {
|
||||||
|
out[i] = in[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( gate->cur_state ) {
|
||||||
|
gate->cur_state = false;
|
||||||
|
printf("GATE off\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for( size_t i = 0; i < frame_size; ++i ) {
|
||||||
|
out[i] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
gate.h
Normal file
21
gate.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct gate {
|
||||||
|
float threshold;
|
||||||
|
bool enable_calibration;
|
||||||
|
|
||||||
|
float cur_avg;
|
||||||
|
bool cur_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
void gate_init();
|
||||||
|
void gate_process(
|
||||||
|
struct gate * gate,
|
||||||
|
size_t frame_size,
|
||||||
|
float const * in,
|
||||||
|
float * out
|
||||||
|
);
|
80
guitfx.c
80
guitfx.c
|
@ -14,7 +14,10 @@
|
||||||
#include <pipewire/filter.h>
|
#include <pipewire/filter.h>
|
||||||
|
|
||||||
#include "delay.h"
|
#include "delay.h"
|
||||||
|
#include "sust.h"
|
||||||
|
#include "gate.h"
|
||||||
|
|
||||||
|
float envelope( float x );
|
||||||
struct data;
|
struct data;
|
||||||
|
|
||||||
struct port {
|
struct port {
|
||||||
|
@ -31,6 +34,10 @@ struct data {
|
||||||
//! effect data
|
//! effect data
|
||||||
uint64_t last_tap;
|
uint64_t last_tap;
|
||||||
struct delay delay;
|
struct delay delay;
|
||||||
|
struct sust sust;
|
||||||
|
struct gate gate;
|
||||||
|
|
||||||
|
unsigned prog;
|
||||||
|
|
||||||
//! elapsed time in number of samples
|
//! elapsed time in number of samples
|
||||||
uint64_t time;
|
uint64_t time;
|
||||||
|
@ -82,28 +89,72 @@ static void on_process(void *userdata, struct spa_io_position *position)
|
||||||
switch( midi_data[1] ) {
|
switch( midi_data[1] ) {
|
||||||
case 0x0b:
|
case 0x0b:
|
||||||
// expr pedal
|
// expr pedal
|
||||||
float val_f = ((float)midi_data[2]) / 128.0;
|
float val_f = ((float)midi_data[2]) / 128.0;
|
||||||
printf("Expr Pedal %f\n", val_f);
|
|
||||||
|
|
||||||
data->delay.mix = val_f;
|
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;
|
break;
|
||||||
|
|
||||||
case 0x42:
|
case 0x42:
|
||||||
// sust pedal
|
|
||||||
|
|
||||||
uint64_t cur_tap = frame + c->offset;
|
// noise gate calibration
|
||||||
uint64_t duration = cur_tap - data->last_tap;
|
if( data->prog == 2 ) {
|
||||||
data->last_tap = cur_tap;
|
if( midi_data[2] >= 64) {
|
||||||
if( duration < (4*position->clock.rate.denom) ) {
|
data->gate.threshold = 0.0;//data->gate.cur_avg;
|
||||||
delay_set_time( &data->delay, duration/4 );
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xc0:
|
case 0xc0:
|
||||||
// program change
|
// 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]);
|
printf("program change: %u\n", midi_data[1]);
|
||||||
|
data->prog = midi_data[1];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -124,9 +175,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 * 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);
|
float * out = pw_filter_get_dsp_buffer(data->out_port, n_samples);
|
||||||
if( in && out ) {
|
if( in && out ) {
|
||||||
delay_process( &data->delay, n_samples, in, out );
|
gate_process( &data->gate, n_samples, in, tmp1 );
|
||||||
|
sust_process( &data->sust, n_samples, in, tmp2 );
|
||||||
|
delay_process( &data->delay, n_samples, tmp2, out );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +200,10 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct data data = { 0, };
|
struct data data = { 0, };
|
||||||
|
|
||||||
|
gate_init( &data.gate );
|
||||||
delay_init( &data.delay );
|
delay_init( &data.delay );
|
||||||
|
sust_init( &data.sust );
|
||||||
|
data.prog = 0;
|
||||||
|
|
||||||
const struct spa_pod *params[1];
|
const struct spa_pod *params[1];
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
project('guitfx', 'c')
|
project('guitfx', 'c')
|
||||||
pipewire_dep = dependency('libpipewire-0.3')
|
pipewire_dep = dependency('libpipewire-0.3')
|
||||||
|
|
||||||
|
cc = meson.get_compiler('c')
|
||||||
|
m_dep = cc.find_library('m', required : false)
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
'guitfx',
|
'guitfx',
|
||||||
'guitfx.c',
|
'guitfx.c',
|
||||||
'delay.c',
|
'delay.c',
|
||||||
dependencies : [pipewire_dep],
|
'sust.c',
|
||||||
|
'gate.c',
|
||||||
|
dependencies : [pipewire_dep, m_dep],
|
||||||
)
|
)
|
||||||
|
|
87
sust.c
Normal file
87
sust.c
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#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 = 51200;
|
||||||
|
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_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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
32
sust.h
Normal file
32
sust.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#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_resize( struct sust * sust, size_t new_len );
|
||||||
|
void sust_swap( struct sust * sust );
|
||||||
|
|
||||||
|
void sust_process(
|
||||||
|
struct sust * sust,
|
||||||
|
size_t frame_size,
|
||||||
|
float const * in,
|
||||||
|
float * out
|
||||||
|
);
|
Loading…
Reference in a new issue