~rantlivelintkale/kantan.lv2

8945da69bb74304d3c27ef00c339a286a43b4811 — Veikka Valtteri Kallinen 4 months ago 4b92cca
improve algorithm

Change offset for a better asymmetry control, that changes the shape of
the input signal by setting the amount of distortion for the negative
values without affecting the positive values.

Calculate post gain using RMS like with polynomial drive.
2 files changed, 64 insertions(+), 24 deletions(-)

M lv2ttl/kantan.ttl
M src/main.c
M lv2ttl/kantan.ttl => lv2ttl/kantan.ttl +6 -6
@@ 37,18 37,18 @@
                a lv2:InputPort ,
                        lv2:ControlPort ;
                lv2:index 2 ;
                lv2:symbol "gain" ;
                lv2:name "Gain" ;
                lv2:symbol "dist" ;
                lv2:name "Distortion" ;
                lv2:default 0.0 ;
                lv2:minimum 0.0 ;
                lv2:maximum 10.0 ;
                lv2:maximum 100.0 ;
        ] , [
                a lv2:InputPort ,
                        lv2:ControlPort ;
                lv2:index 3 ;
                lv2:symbol "offset" ;
                lv2:name "Offset" ;
                lv2:symbol "asym" ;
                lv2:name "Asymmetry" ;
                lv2:default 0.0 ;
                lv2:minimum 0.0 ;
                lv2:maximum 0.5 ;
                lv2:maximum 100.0 ;
        ] .

M src/main.c => src/main.c +58 -18
@@ 10,11 10,12 @@
typedef enum {
	INPUT = 0,
	OUTPUT = 1,
	GAIN = 2,
	OFFSET = 3,
	DIST = 2,
	ASYM = 3,
} PortIndex;

typedef struct {
	const float r;
	float prev_x;
	float prev_y;
} DCFilter;


@@ 22,22 23,58 @@ typedef struct {
typedef struct {
	const float *input;
	float *output;
	const float *gain;
	const float *offset;
	const float *dist;
	const float *asym;

	DCFilter dc_filter_rms;
	DCFilter dc_filter;
} Kantan;

static float blockdc(const float x, DCFilter* filter)
{
	const float r = filter->r;
	const float prev_x = filter->prev_x;
	const float prev_y = filter->prev_y;
	const float y = x - prev_x + 0.995f*prev_y;
	const float y = x - prev_x + r*prev_y;
	filter->prev_x = x;
	filter->prev_y = y;
	return y;
}

static float distort(const float x, const float d, const float a)
{
	float k;
	if (x > 0.f) {
		k = d*(1.f - a * 0.01);
	} else {
		k = d;
	}

	float y;
	if (k < 0.01f) {
		y = x;
	} else {
		y = 1 / k * atanf(k * x);
	}

	return y;
}

static float rms(const float d, const float a, DCFilter *filter)
{
	float accm = 0;
	for (uint32_t i = 0; i < 49; i++) {
		const float x = i / 48.f;
		const float y = distort(0.1f * sin(2.f * M_PI * x), d, a);
		const float y_nodc = blockdc(y, filter);
		accm += y_nodc*y_nodc;
	}
	
	const float result = sqrtf(accm / 49);
	
	return result;
}

static LV2_Handle instantiate(const LV2_Descriptor * descriptor,
		double rate,
		const char *bundle_path,


@@ 46,7 83,12 @@ static LV2_Handle instantiate(const LV2_Descriptor * descriptor,

	Kantan *kantan = (Kantan *) calloc(1, sizeof(Kantan));

	DCFilter dc_filter_init = {0,0};
	const float wn = M_PI * 10.f / rate;
	const float b0 = 1.f / (1.f + wn);
	const float p = (1.f - wn) * b0;

	DCFilter dc_filter_init = {p,0.f,0.f};
	memcpy(&kantan->dc_filter_rms, &dc_filter_init, sizeof(DCFilter));
	memcpy(&kantan->dc_filter, &dc_filter_init, sizeof(DCFilter));

	return (LV2_Handle) kantan;


@@ 63,11 105,11 @@ static void connect_port(LV2_Handle instance, uint32_t port, void *data)
		case OUTPUT:
			kantan->output = (float *)data;
			break;
		case GAIN:
			kantan->gain = (const float *)data;
		case DIST:
			kantan->dist = (const float *)data;
			break;
		case OFFSET:
			kantan->offset = (const float *)data;
		case ASYM:
			kantan->asym = (const float *)data;
			break;
	}
}


@@ 82,18 124,16 @@ static void run(LV2_Handle instance, uint32_t n_samples)

	const float *const input = kantan->input;
	float *const output = kantan->output;
	const float gain = *(kantan->gain);
	const float offset = *(kantan->offset);
	const float dist = *(kantan->dist);
	const float asym = *(kantan->asym);
	DCFilter *dc_filter_rms = &kantan->dc_filter_rms;
	DCFilter* dc_filter = &kantan->dc_filter;

	const float g_pre = pow (10.0, (gain * 0.5f));
	const float g_post = 0.1f / atan(0.1f * g_pre);
	const float rms_ref = 0.1f / sqrtf(2);
	const float g_post = rms_ref / rms(dist, asym, dc_filter_rms);

	/**
	  distortion using sigmoid function: 2 / pi * arctan (pi / 2 * x)
	  */
	for (uint32_t pos = 0; pos < n_samples; pos++) {
		output[pos] = blockdc(atan((input[pos] * g_pre + offset)), dc_filter) * g_post;
		output[pos] = blockdc(distort(input[pos], dist, asym), dc_filter) * g_post;
	}
}