Skip to content

Commit

Permalink
Change low_pass to a general frequency_pass segment with both high an…
Browse files Browse the repository at this point in the history
…d low filtering. (#2)
  • Loading branch information
Shinmera committed Nov 17, 2017
1 parent 631982e commit 84c1cd7
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 217 deletions.
15 changes: 12 additions & 3 deletions src/mixed.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,10 @@ extern "C" {
MIXED_REPEAT_MODE,
// Access the frequency cutoff of the low/high-pass filter.
// This value is a size_t in Hertz.
MIXED_FREQUENCY_CUTOFF
MIXED_FREQUENCY_CUTOFF,
// Access whether to pass frequencies above or below the cutoff.
// This value is an enum mixed_frequency_pass.
MIXED_FREQUENCY_PASS
};

// This enum describes the possible preset attenuation functions.
Expand Down Expand Up @@ -285,6 +288,12 @@ extern "C" {
MIXED_PLAY
};

// This enum describes the possible pass directions.
MIXED_EXPORT enum mixed_frequency_pass{
MIXED_PASS_LOW,
MIXED_PASS_HIGH
};

// This enum holds property flags for segments.
MIXED_EXPORT enum mixed_segment_info_flags{
// This means that the segment's output and input
Expand Down Expand Up @@ -848,12 +857,12 @@ extern "C" {
// This segment can generate white, pink, and brown noise.
MIXED_EXPORT int mixed_make_segment_noise(enum mixed_noise_type, struct mixed_segment *segment);

// A low-pass frequency filter segment.
// A frequency filter segment.
//
// The cutoff cannot be larger than the samplerate. Generally, if the cutoff
// frequency is larger than half of the samplerate, major distortion will
// occur, so tread carefully.
MIXED_EXPORT int mixed_make_segment_low_pass(size_t cutoff, size_t samplerate, struct mixed_segment *segment);
MIXED_EXPORT int mixed_make_segment_frequency_pass(enum mixed_frequency_pass pass, size_t cutoff, size_t samplerate, struct mixed_segment *segment);

// Free the associated sequence data.
MIXED_EXPORT void mixed_free_segment_sequence(struct mixed_segment_sequence *mixer);
Expand Down
256 changes: 256 additions & 0 deletions src/segments/frequency_pass.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
#include "internal.h"

struct frequency_pass_segment_data{
struct mixed_buffer *in;
struct mixed_buffer *out;
float x[2];
float y[2];
float a[2];
float b[2];
float k;
size_t cutoff;
size_t samplerate;
enum mixed_frequency_pass pass;
};

void compute_coefficients(struct frequency_pass_segment_data *data){
float a = tan(M_PI*data->cutoff/data->samplerate);
float a2 = a*a;
float s2 = sqrt(2);
data->k = (a2)/(1+s2*a+a2);
data->a[0] = (2*(a2-1))/(1+s2*a+a2);
data->a[1] = (1-s2*a+a2)/(1+s2*a+a2);
data->b[0] = 2;
data->b[1] = 1;
data->x[0] = 0;
data->x[1] = 0;
data->y[0] = 0;
data->y[1] = 0;
}

int frequency_pass_segment_free(struct mixed_segment *segment){
if(segment->data){
free(segment->data);
}
segment->data = 0;
return 1;
}

// FIXME: add start method that checks for buffer completeness.

int frequency_pass_segment_start(struct mixed_segment *segment){
struct frequency_pass_segment_data *data = (struct frequency_pass_segment_data *)segment->data;
data->x[0] = 0;
data->x[1] = 0;
data->y[0] = 0;
data->y[1] = 0;
return 1;
}

int frequency_pass_segment_set_in(size_t field, size_t location, void *buffer, struct mixed_segment *segment){
struct frequency_pass_segment_data *data = (struct frequency_pass_segment_data *)segment->data;

switch(field){
case MIXED_BUFFER:
if(location == 0){
data->in = (struct mixed_buffer *)buffer;
return 1;
}
mixed_err(MIXED_INVALID_LOCATION);
return 0;
default:
mixed_err(MIXED_INVALID_FIELD);
return 0;
}
}

int frequency_pass_segment_set_out(size_t field, size_t location, void *buffer, struct mixed_segment *segment){
struct frequency_pass_segment_data *data = (struct frequency_pass_segment_data *)segment->data;

switch(field){
case MIXED_BUFFER:
if(location == 0){
data->out = (struct mixed_buffer *)buffer;
return 1;
}
mixed_err(MIXED_INVALID_LOCATION);
return 0;
default:
mixed_err(MIXED_INVALID_FIELD);
return 0;
}
}

void low_pass_segment_mix(size_t samples, struct mixed_segment *segment){
struct frequency_pass_segment_data *data = (struct frequency_pass_segment_data *)segment->data;

float *in = data->in->data;
float *out = data->out->data;
float *x = data->x;
float *y = data->y;
float *a = data->a;
float *b = data->b;
float k = data->k;

for(size_t i=0; i<samples; ++i){
float s = in[i];
out[i] = k*s + k*b[0]*x[0] + k*b[1]*x[1] - a[0]*y[0] - a[1]*y[1];
x[1] = x[0];
x[0] = s;
y[1] = y[0];
y[0] = out[i];
}
}

void high_pass_segment_mix(size_t samples, struct mixed_segment *segment){
struct frequency_pass_segment_data *data = (struct frequency_pass_segment_data *)segment->data;

float *in = data->in->data;
float *out = data->out->data;
float *x = data->x;
float *y = data->y;
float *a = data->a;
float *b = data->b;
float k = data->k;

for(size_t i=0; i<samples; ++i){
float s = in[i];
out[i] = k*s + k*b[0]*x[0] + k*b[1]*x[1] - a[0]*y[0] - a[1]*y[1];
x[1] = x[0];
x[0] = s;
y[1] = y[0];
y[0] = out[i];
out[i] = s-out[i];
}
}

void frequency_pass_segment_mix_bypass(size_t samples, struct mixed_segment *segment){
struct frequency_pass_segment_data *data = (struct frequency_pass_segment_data *)segment->data;

mixed_buffer_copy(data->in, data->out);
}

struct mixed_segment_info *frequency_pass_segment_info(struct mixed_segment *segment){
struct frequency_pass_segment_data *data = (struct frequency_pass_segment_data *)segment->data;
struct mixed_segment_info *info = calloc(1, sizeof(struct mixed_segment_info));

if(info){
info->name = "frequency_pass";
info->description = "A frequency filter segment.";
info->flags = MIXED_INPLACE;
info->min_inputs = 1;
info->max_inputs = 1;
info->outputs = 1;

info->fields[0].field = MIXED_BUFFER;
info->fields[0].description = "The buffer for audio data attached to the location.";
info->fields[0].flags = MIXED_IN | MIXED_OUT | MIXED_SET;

info->fields[1].field = MIXED_FREQUENCY_CUTOFF;
info->fields[1].description = "The maximum/minimum frequency that will pass through the segment.";
info->fields[1].flags = MIXED_SEGMENT | MIXED_SET | MIXED_GET;

info->fields[2].field = MIXED_FREQUENCY_PASS;
info->fields[2].description = "Whether to pass high or low frequencies.";
info->fields[2].flags = MIXED_SEGMENT | MIXED_SET | MIXED_GET;

info->fields[3].field = MIXED_SAMPLERATE;
info->fields[3].description = "The samplerate at which the segment operates.";
info->fields[3].flags = MIXED_SEGMENT | MIXED_SET | MIXED_GET;

info->fields[4].field = MIXED_BYPASS;
info->fields[4].description = "Bypass the segment's processing.";
info->fields[4].flags = MIXED_SEGMENT | MIXED_SET | MIXED_GET;
}

return info;
}

int frequency_pass_segment_get(size_t field, void *value, struct mixed_segment *segment){
struct frequency_pass_segment_data *data = (struct frequency_pass_segment_data *)segment->data;
switch(field){
case MIXED_SAMPLERATE: *((size_t *)value) = data->samplerate; break;
case MIXED_FREQUENCY_CUTOFF: *((size_t *)value) = data->cutoff; break;
case MIXED_FREQUENCY_PASS: *((enum mixed_frequency_pass *)value) = data->pass; break;
case MIXED_BYPASS: *((bool *)value) = (segment->mix == frequency_pass_segment_mix_bypass); break;
default: mixed_err(MIXED_INVALID_FIELD); return 0;
}
return 1;
}

int frequency_pass_segment_set(size_t field, void *value, struct mixed_segment *segment){
struct frequency_pass_segment_data *data = (struct frequency_pass_segment_data *)segment->data;
switch(field){
case MIXED_SAMPLERATE:
if(*(size_t *)value <= 0){
mixed_err(MIXED_INVALID_VALUE);
return 0;
}
data->samplerate = *(size_t *)value;
compute_coefficients(data);
break;
case MIXED_FREQUENCY_CUTOFF:
if(*(size_t *)value <= 0 || data->samplerate <= *(size_t *)value){
mixed_err(MIXED_INVALID_VALUE);
return 0;
}
data->cutoff = *(size_t *)value;
compute_coefficients(data);
break;
case MIXED_FREQUENCY_PASS:
if(*(enum mixed_frequency_pass *)value < MIXED_PASS_LOW ||
MIXED_PASS_HIGH < *(enum mixed_frequency_pass *)value){
mixed_err(MIXED_INVALID_VALUE);
return 0;
}
data->pass = *(enum mixed_frequency_pass *)value;
if(segment->mix != frequency_pass_segment_mix_bypass){
segment->mix = (data->pass == MIXED_PASS_LOW)
? low_pass_segment_mix
: high_pass_segment_mix;
}
case MIXED_BYPASS:
if(*(bool *)value){
segment->mix = frequency_pass_segment_mix_bypass;
}else if(data->pass == MIXED_PASS_LOW){
segment->mix = low_pass_segment_mix;
}else{
segment->mix = high_pass_segment_mix;
}
break;
default:
mixed_err(MIXED_INVALID_FIELD);
return 0;
}
return 1;
}

MIXED_EXPORT int mixed_make_segment_frequency_pass(enum mixed_frequency_pass pass, size_t cutoff, size_t samplerate, struct mixed_segment *segment){
if(samplerate <= cutoff){
mixed_err(MIXED_INVALID_VALUE);
return 0;
}

struct frequency_pass_segment_data *data = calloc(1, sizeof(struct frequency_pass_segment_data));
if(!data){
mixed_err(MIXED_OUT_OF_MEMORY);
return 0;
}

data->cutoff = cutoff;
data->samplerate = samplerate;
data->pass = pass;

compute_coefficients(data);

segment->free = frequency_pass_segment_free;
segment->start = frequency_pass_segment_start;
segment->mix = (pass == MIXED_PASS_LOW)? low_pass_segment_mix : high_pass_segment_mix;
segment->set_in = frequency_pass_segment_set_in;
segment->set_out = frequency_pass_segment_set_out;
segment->info = frequency_pass_segment_info;
segment->get = frequency_pass_segment_get;
segment->set = frequency_pass_segment_set;
segment->data = data;
return 1;
}
Loading

0 comments on commit 84c1cd7

Please sign in to comment.