Skip to content

Commit

Permalink
Add (untested) noise gate segment (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shinmera committed Nov 16, 2017
1 parent 9c7d68d commit c28cb56
Show file tree
Hide file tree
Showing 2 changed files with 320 additions and 1 deletion.
26 changes: 25 additions & 1 deletion src/mixed.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,27 @@ extern "C" {
// Access the sample rate by which the segment operates.
MIXED_SAMPLERATE,
// Access the change of the pitch as a float.
MIXED_PITCH_SHIFT
MIXED_PITCH_SHIFT,
// Access the opening threshold for the gate as a float.
// The threshold is in dB.
// The default is -24dB
MIXED_GATE_OPEN_THRESHOLD,
// Access the closing threshold for the gate as a float.
// The threshold is in dB.
// The default is -32dB
MIXED_GATE_CLOSE_THRESHOLD,
// Access the attack time for the gate as a float.
// The attack time is in seconds.
// The default is 0.025s
MIXED_GATE_ATTACK,
// Access the hold time for the gate as a float.
// The hold time is in seconds.
// The default is 0.2s
MIXED_GATE_HOLD,
// Access the release time for the gate as a float.
// The release time is in seconds.
// The default is 0.15s
MIXED_GATE_RELEASE
};

// This enum describes the possible preset attenuation functions.
Expand Down Expand Up @@ -780,6 +800,10 @@ extern "C" {
// pitch and so on.
MIXED_EXPORT int mixed_make_segment_pitch(float pitch, size_t samplerate, struct mixed_segment *segment);

// A noise gate segment
//
MIXED_EXPORT int mixed_make_segment_gate(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
295 changes: 295 additions & 0 deletions src/segments/gate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
#include "internal.h"

struct gate_segment_data{
struct mixed_buffer *in;
struct mixed_buffer *out;
float close_threshold;
float open_threshold;
float attack;
float hold;
float release;
size_t samplerate;
float time;
enum state state;
};

enum state{
CLOSED,
ATTACKING,
OPEN,
HOLDING,
RELEASING,
};

float db_to_linear(float db){
return pow(db/20.0);
}

float linear_to_db(float linear){
return log10(linear)*20;
}

int gate_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 gate_segment_start(struct mixed_segment *segment){
struct gate_segment_data *data = (struct gate_segment_data *)segment->data;
data->time = 0.0;
return 1;
}

int gate_segment_set_in(size_t field, size_t location, void *buffer, struct mixed_segment *segment){
struct gate_segment_data *data = (struct gate_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 gate_segment_set_out(size_t field, size_t location, void *buffer, struct mixed_segment *segment){
struct gate_segment_data *data = (struct gate_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 gate_segment_mix(size_t samples, struct mixed_segment *segment){
struct gate_segment_data *data = (struct gate_segment_data *)segment->data;

float stime = 1.0/data->samplerate;
float time = data->time;
float open = data->open;
float close = data->close;
float attack = data->attack;
float hold = data->hold;
float release = data->release;
float *in = data->in->data;
float *out = data->out->data;
float volume = 0.0;
for(size_t i=0; i<samples; ++i){
float sample = in[i];
switch(data->state){
case CLOSED:
if(sample < open){
volume = 0.0;
break;
}else{
time = 0.0;
data->state = ATTACKING;
}
case ATTACKING:
if(time < attack){
time += stime;
volume = time/attack;
break;
}else{
data->state = OPEN;
}
case OPEN:
if(close < sample){
volume = 1.0;
break;
}else{
time = hold;
data->state = HOLDING;
}
case HOLDING:
if(0 < time){
time -= stime;
volume = 1.0;
break;
}else{
time = release;
data->state = RELEASING;
}
case RELEASING:
if(open <= sample){
data->state = ATTACKING;
volume = time/release;
}else if(0 < time){
time -= stime;
volume = time/release;
}else{
time = 0.0;
volume = 0.0;
data->state = CLOSED;
}
}
out[i] = sample * volume;
}
data->time = time;
}

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

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

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

if(info){
info->name = "gate";
info->description = "A noise gate segment to filter out low-volume frequencies.";
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_GATE_OPEN_THRESHOLD;
info->fields[1].description = "The volume in dB necessary to open the gate.";
info->fields[1].flags = MIXED_SEGMENT | MIXED_SET | MIXED_GET;

info->fields[1].field = MIXED_GATE_CLOSE_THRESHOLD;
info->fields[1].description = "The volume in dB below which the gate is closed again.";
info->fields[1].flags = MIXED_SEGMENT | MIXED_SET | MIXED_GET;

info->fields[1].field = MIXED_GATE_ATTACK;
info->fields[1].description = "The time during which the output volume is scaled up.";
info->fields[1].flags = MIXED_SEGMENT | MIXED_SET | MIXED_GET;

info->fields[1].field = MIXED_GATE_HOLD;
info->fields[1].description = "The time during which the output is still transmitted despite the gate being closed.";
info->fields[1].flags = MIXED_SEGMENT | MIXED_SET | MIXED_GET;

info->fields[1].field = MIXED_GATE_RELEASE;
info->fields[1].description = "The time during which the output volume is scaled down.";
info->fields[1].flags = MIXED_SEGMENT | MIXED_SET | MIXED_GET;

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

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

return info;
}

int gate_segment_get(size_t field, void *value, struct mixed_segment *segment){
struct gate_segment_data *data = (struct gate_segment_data *)segment->data;
switch(field){
case MIXED_GATE_OPEN_THRESHOLD: *((float *)value) = linear_to_db(data->open_threshold); break;
case MIXED_GATE_CLOSE_THRESHOLD: *((float *)value) = linear_to_db(data->close_threshold); break;
case MIXED_GATE_ATTACK: *((float *)value) = data->attack; break;
case MIXED_GATE_HOLD: *((float *)value) = data->hold; break;
case MIXED_GATE_RELEASE: *((float *)value) = data->release; break;
case MIXED_SAMPLERATE: *((size_t *)value) = data->samplerate; break;
case MIXED_BYPASS: *((bool *)value) = (segment->mix == gate_segment_mix_bypass); break;
default: mixed_err(MIXED_INVALID_FIELD); return 0;
}
return 1;
}

int gate_segment_set(size_t field, void *value, struct mixed_segment *segment){
struct gate_segment_data *data = (struct gate_segment_data *)segment->data;
switch(field){
case MIXED_SAMPLERATE:
if(*(size_t *)value <= 0){
mixed_err(MIXED_INVALID_VALUE);
return 0;
}
free_gate_data(&data->gate_data);
if(!make_gate_data(2048, 4, data->samplerate, &data->gate_data)){
return 0;
}
break;
case MIXED_GATE_OPEN_THRESHOLD:
data->open_threshold = db_to_linear(*(float *)value);
break;
case MIXED_GATE_CLOSE_THRESHOLD:
data->close_threshold = db_to_linear(*(float *)value);
break;
case MIXED_GATE_ATTACK:
if(*(float *)value < 0.0){
mixed_err(MIXED_INVALID_VALUE);
return 0;
}
data->attack = *(float *)value;
break;
case MIXED_GATE_HOLD:
if(*(float *)value < 0.0){
mixed_err(MIXED_INVALID_VALUE);
return 0;
}
data->hold = *(float *)value;
break;
case MIXED_GATE_RELEASE:
if(*(float *)value < 0.0){
mixed_err(MIXED_INVALID_VALUE);
return 0;
}
data->release = *(float *)value;
break;
case MIXED_BYPASS:
if(*(bool *)value){
segment->mix = gate_segment_mix_bypass;
}else{
segment->mix = gate_segment_mix;
}
break;
default:
mixed_err(MIXED_INVALID_FIELD);
return 0;
}
return 1;
}

MIXED_EXPORT int mixed_make_segment_gate(float gate, size_t samplerate, struct mixed_segment *segment){
struct gate_segment_data *data = calloc(1, sizeof(struct gate_segment_data));
if(!data){
mixed_err(MIXED_OUT_OF_MEMORY);
return 0;
}

data->open_threshold = db_to_linear(-24.0);
data->close_threshold = db_to_linear(-32.0);
data->attack = 0.025;
data->hold = 0.2;
data->release = 0.15;

segment->free = gate_segment_free;
segment->start = gate_segment_start;
segment->mix = gate_segment_mix;
segment->set_in = gate_segment_set_in;
segment->set_out = gate_segment_set_out;
segment->info = gate_segment_info;
segment->get = gate_segment_get;
segment->set = gate_segment_set;
segment->data = data;
return 1;
}

0 comments on commit c28cb56

Please sign in to comment.