added LFO, made Envelope generator actually do things.
parent
16f7e0664f
commit
680053eb88
|
@ -17,4 +17,5 @@ typedef struct EngineState {
|
||||||
uint32_t t;
|
uint32_t t;
|
||||||
int sample_rate;
|
int sample_rate;
|
||||||
} EngineState;
|
} EngineState;
|
||||||
|
typedef enum Waveform {_waveform_sine, _waveform_saw, _waveform_square} Waveform;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,28 +15,29 @@
|
||||||
#include "../utils/envelope.h"
|
#include "../utils/envelope.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
typedef enum Waveform {_waveform_sine, _waveform_saw, _waveform_square} Waveform;
|
|
||||||
typedef struct Basic {
|
typedef struct Basic {
|
||||||
float freq;
|
float freq;
|
||||||
Waveform shape;
|
Waveform shape;
|
||||||
int gate;
|
int gate;
|
||||||
Envelope Env;
|
Envelope env;
|
||||||
float pwm;
|
float pwm;
|
||||||
|
float amp;
|
||||||
float(*process)(struct Basic *, EngineState*);
|
float(*process)(struct Basic *, EngineState*);
|
||||||
} Basic;
|
} Basic;
|
||||||
|
|
||||||
|
|
||||||
float basic_process(Basic *self, EngineState *t) {
|
float basic_process(Basic *self, EngineState *t) {
|
||||||
float out;
|
float out;
|
||||||
|
self->env.gate = self->gate;
|
||||||
switch (self->shape) {
|
switch (self->shape) {
|
||||||
case _waveform_saw:
|
case _waveform_saw:
|
||||||
out = fmod((self->freq+1) * t->t / 44100,1.0);
|
out = (self->amp * self->env.process(&(self->env),t)) * fmod((self->freq+1) * t->t / 44100,1.0);
|
||||||
break;
|
break;
|
||||||
case _waveform_sine:
|
case _waveform_sine:
|
||||||
out = sin(2 * M_PI * t->t * self->freq / 44100);
|
out = (self->amp * self->env.process(&(self->env),t)) * sin(2 * M_PI * t->t * self->freq / 44100);
|
||||||
break;
|
break;
|
||||||
case _waveform_square:
|
case _waveform_square:
|
||||||
out = fmod((self->freq+1) * t->t / 44100,1.0) > self->pwm;
|
out = (self->amp * self->env.process(&(self->env),t)) * fmod((self->freq+1) * t->t / 44100,1.0) > self->pwm;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
out = 0.0F;
|
out = 0.0F;
|
||||||
|
@ -47,8 +48,10 @@ float basic_process(Basic *self, EngineState *t) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Basic* basic_new(Waveform w, Envelope e) {
|
Basic* basic_new(Waveform w, Envelope e) {
|
||||||
Basic *b = malloc(sizeof(Envelope));
|
Basic *b = malloc(sizeof(Basic));
|
||||||
b->shape = w;
|
b->shape = w;
|
||||||
|
b->env = e;
|
||||||
|
b->amp = 0;
|
||||||
b->process = basic_process;
|
b->process = basic_process;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
#ifndef _H_BASIC
|
#ifndef _H_BASIC
|
||||||
#define _H_BASIC
|
#define _H_BASIC
|
||||||
|
|
||||||
typedef enum Waveform {_waveform_sine, _waveform_saw, _waveform_square} Waveform;
|
|
||||||
typedef struct Basic {
|
typedef struct Basic {
|
||||||
float freq;
|
float freq;
|
||||||
Waveform shape;
|
Waveform shape;
|
||||||
int gate;
|
int gate;
|
||||||
Envelope Env;
|
Envelope Env;
|
||||||
float pwm;
|
float pwm;
|
||||||
|
float amp;
|
||||||
float(*process)(struct Basic *, EngineState*);
|
float(*process)(struct Basic *, EngineState*);
|
||||||
} Basic;
|
} Basic;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "synths/basic.h"
|
#include "synths/basic.h"
|
||||||
#include "filters/svf.h"
|
#include "filters/svf.h"
|
||||||
#include "utils/envelope.h"
|
#include "utils/envelope.h"
|
||||||
|
#include "utils/lfo.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
|
|
||||||
Basic saw;
|
Basic saw;
|
||||||
SVF filt;
|
SVF filt;
|
||||||
|
LFO lfo;
|
||||||
static EngineState data;
|
static EngineState data;
|
||||||
|
|
||||||
int mycallback( const void *input,
|
int mycallback( const void *input,
|
||||||
|
@ -84,9 +86,15 @@ int main(int argc, char**argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Envelope b = *envelope_new();
|
Envelope b = *envelope_new();
|
||||||
saw = *basic_new(_waveform_square, b);
|
b.attack = 44100*2;
|
||||||
|
b.release = 44100/2;
|
||||||
|
b.ar = true;
|
||||||
|
b.sustain = true;
|
||||||
|
saw = *basic_new(_waveform_sine, b);
|
||||||
|
saw.amp = 1.0f;
|
||||||
saw.pwm = 0.5;
|
saw.pwm = 0.5;
|
||||||
saw.freq = 440;
|
saw.freq = 440;
|
||||||
|
saw.gate = false;
|
||||||
filt = *svf_new(7000.0f, 0.5f);
|
filt = *svf_new(7000.0f, 0.5f);
|
||||||
//data.sample_rate=SAMPLE_RATE;
|
//data.sample_rate=SAMPLE_RATE;
|
||||||
PaStream *stream;
|
PaStream *stream;
|
||||||
|
@ -105,11 +113,9 @@ int main(int argc, char**argv) {
|
||||||
if( err != paNoError ) goto error;
|
if( err != paNoError ) goto error;
|
||||||
/* start anything intersting */
|
/* start anything intersting */
|
||||||
saw.gate = true;
|
saw.gate = true;
|
||||||
for (int freq = 7000; freq >= 60; freq = freq - 0.0001) {
|
sleep(5);
|
||||||
filt.freq = freq;
|
saw.gate = false;
|
||||||
usleep(500);
|
sleep(5);
|
||||||
}
|
|
||||||
sleep(1);
|
|
||||||
|
|
||||||
/* more crap */
|
/* more crap */
|
||||||
error:
|
error:
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum EnvState {_e_off,_e_attack, _e_attackrelease, _e_decay, _e_sustain,_e_release, _e_finished} EnvState;
|
||||||
typedef struct Envelope {
|
typedef struct Envelope {
|
||||||
// these values are in samples.
|
// these values are in samples.
|
||||||
unsigned int attack;
|
unsigned int attack;
|
||||||
|
@ -24,18 +26,137 @@ typedef struct Envelope {
|
||||||
float sustain_level;
|
float sustain_level;
|
||||||
float max_level;
|
float max_level;
|
||||||
float min_level;
|
float min_level;
|
||||||
|
float level;
|
||||||
bool gate;
|
bool gate;
|
||||||
|
unsigned int t;
|
||||||
|
EnvState envstate;
|
||||||
|
bool ar;
|
||||||
|
bool sustain;
|
||||||
float(*process)(struct Envelope *,EngineState *);
|
float(*process)(struct Envelope *,EngineState *);
|
||||||
|
|
||||||
} Envelope;
|
} Envelope;
|
||||||
|
/*
|
||||||
|
|
||||||
|
if (self->ar) {
|
||||||
|
float diff = (self->max_level - self->min_level) / (float) self->attack;
|
||||||
|
if (self->t <= self->attack && self->gate) {
|
||||||
|
self->level = self->state * diff;
|
||||||
|
self->state++;
|
||||||
|
} else if (self->sustain && self->gate) {
|
||||||
|
self->level = self->max_level;
|
||||||
|
} else if (self->state < self->attack && self->state > 0){
|
||||||
|
self->level = self->level - diff;
|
||||||
|
self->state--;
|
||||||
|
} else if ((self->state - self->attack) <= self->release) {
|
||||||
|
self->level = self->max_level - ((self->state - self->attack) * diff);
|
||||||
|
self->state++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self->level;
|
||||||
|
return self->gate ? self->max_level : self-> min_level;
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
float envelope_process(Envelope *self, EngineState *t) {
|
float envelope_process(Envelope *self, EngineState *t) {
|
||||||
return self->gate ? self->max_level : self-> min_level;
|
switch (self->envstate) {
|
||||||
|
case _e_off:
|
||||||
|
if (self->gate) {
|
||||||
|
self->t = 0;
|
||||||
|
self->envstate = _e_attack;
|
||||||
|
return envelope_process(self, t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case _e_attack:
|
||||||
|
if (self->gate) {
|
||||||
|
if ( self->t < self->attack ) {
|
||||||
|
float stepsize = (self->max_level - self->min_level) / self->attack;
|
||||||
|
self->t++;
|
||||||
|
return self->t * stepsize;
|
||||||
|
} else {
|
||||||
|
if (self->ar) {
|
||||||
|
self->t = 0;
|
||||||
|
self->envstate = _e_sustain;
|
||||||
|
return self->max_level;
|
||||||
|
} else {
|
||||||
|
self->t = 0;
|
||||||
|
self->envstate = _e_decay;
|
||||||
|
return self->max_level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self->envstate = _e_attackrelease;
|
||||||
|
return envelope_process(self, t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case _e_decay:
|
||||||
|
if (self->gate) {
|
||||||
|
if (self->t < self->decay) {
|
||||||
|
float stepsize = (self->max_level - self->sustain_level) / self->decay;
|
||||||
|
self->t++;
|
||||||
|
return (self->max_level - (self->t * stepsize));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self->envstate = _e_sustain;
|
||||||
|
return self->sustain_level;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self->envstate = _e_attackrelease;
|
||||||
|
return envelope_process(self, t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case _e_sustain:
|
||||||
|
if (!self->sustain) {
|
||||||
|
self->envstate = _e_release;
|
||||||
|
self->t = 0;
|
||||||
|
return envelope_process(self, t);
|
||||||
|
}
|
||||||
|
if (!self->gate) {
|
||||||
|
self->t = 0;
|
||||||
|
self->envstate = _e_release;
|
||||||
|
if (self->ar)
|
||||||
|
return self->max_level;
|
||||||
|
return self->sustain_level;
|
||||||
|
}
|
||||||
|
if (self->ar)
|
||||||
|
return self->max_level;
|
||||||
|
return self->sustain_level;
|
||||||
|
break;
|
||||||
|
case _e_release:
|
||||||
|
if (self->gate && self->sustain) {
|
||||||
|
// reset envelope.
|
||||||
|
self->envstate=_e_off;
|
||||||
|
return envelope_process(self,t);
|
||||||
|
}
|
||||||
|
if (self->t < self->release) {
|
||||||
|
float stepsize = ((self->ar ? self->max_level : self->sustain_level) - self->min_level) / self->release;
|
||||||
|
self->t++;
|
||||||
|
return (self->ar ? self->max_level : self->sustain_level) - (stepsize * self->t);
|
||||||
|
} else {
|
||||||
|
self->envstate = _e_off;
|
||||||
|
return self->min_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case _e_attackrelease:
|
||||||
|
if (self->gate) {
|
||||||
|
//reset envelope
|
||||||
|
self->envstate=_e_off;
|
||||||
|
return envelope_process(self,t);
|
||||||
|
}
|
||||||
|
return 0.0f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0.0f; // if this is reached, something went wrong or an unhandled function was used.
|
||||||
}
|
}
|
||||||
Envelope* envelope_new() {
|
Envelope* envelope_new() {
|
||||||
Envelope *e = malloc(sizeof(Envelope));
|
Envelope *e = malloc(sizeof(Envelope));
|
||||||
e->process = envelope_process;
|
e->process = envelope_process;
|
||||||
|
e->envstate = _e_off;
|
||||||
|
e->t = 0;
|
||||||
e->max_level = 1.0f;
|
e->max_level = 1.0f;
|
||||||
|
e->sustain_level = 1.0f;
|
||||||
e->min_level = 0.0f;
|
e->min_level = 0.0f;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "../common.h"
|
#include "../common.h"
|
||||||
|
|
||||||
|
typedef enum EnvState {_e_off,_e_attack, _e_attackrelease, _e_decay, _e_sustain,_e_release, _e_finished} EnvState;
|
||||||
typedef struct Envelope {
|
typedef struct Envelope {
|
||||||
// these values are in samples.
|
// these values are in samples.
|
||||||
unsigned int attack;
|
unsigned int attack;
|
||||||
|
@ -24,8 +25,13 @@ typedef struct Envelope {
|
||||||
float sustain_level;
|
float sustain_level;
|
||||||
float max_level;
|
float max_level;
|
||||||
float min_level;
|
float min_level;
|
||||||
|
float level;
|
||||||
bool gate;
|
bool gate;
|
||||||
float(*process)(struct Envelope *, EngineState *);
|
unsigned int t;
|
||||||
|
EnvState envstate;
|
||||||
|
bool ar;
|
||||||
|
bool sustain;
|
||||||
|
float(*process)(struct Envelope *,EngineState *);
|
||||||
|
|
||||||
} Envelope;
|
} Envelope;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Filename: lfo.c
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Version:
|
||||||
|
* Created: Wed Oct 30 17:08:31 2019
|
||||||
|
* Revision: None
|
||||||
|
* Author: Rachel Fae Fox (foxiepaws),fox@foxiepa.ws
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
typedef struct LFO {
|
||||||
|
float freq;
|
||||||
|
Waveform shape;
|
||||||
|
float pwm;
|
||||||
|
float(*process)(struct LFO *, EngineState*);
|
||||||
|
} LFO;
|
||||||
|
|
||||||
|
|
||||||
|
float lfo_process(LFO *self, EngineState *t) {
|
||||||
|
float out;
|
||||||
|
switch (self->shape) {
|
||||||
|
case _waveform_saw:
|
||||||
|
out = fmod((self->freq+1) * t->t / 44100,1.0);
|
||||||
|
break;
|
||||||
|
case _waveform_sine:
|
||||||
|
out = sin(2 * M_PI * t->t * self->freq / 44100);
|
||||||
|
break;
|
||||||
|
case _waveform_square:
|
||||||
|
out = fmod((self->freq+1) * t->t / 44100,1.0) > self->pwm;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
out = 0.0F;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
LFO* lfo_new(Waveform w) {
|
||||||
|
LFO *b = malloc(sizeof(LFO));
|
||||||
|
b->shape = w;
|
||||||
|
b->process = lfo_process;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Filename: lfo.h
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Version:
|
||||||
|
* Created: Wed Oct 30 17:08:31 2019
|
||||||
|
* Revision: None
|
||||||
|
* Author: Rachel Fae Fox (foxiepaws),fox@foxiepa.ws
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _H_LFO
|
||||||
|
#define _H_LFO
|
||||||
|
#include "../common.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
typedef struct LFO {
|
||||||
|
float freq;
|
||||||
|
Waveform shape;
|
||||||
|
float pwm;
|
||||||
|
float(*process)(struct LFO *, EngineState*);
|
||||||
|
} LFO;
|
||||||
|
|
||||||
|
|
||||||
|
float lfo_process(LFO *self, EngineState *t);
|
||||||
|
LFO* lfo_new(Waveform w);
|
||||||
|
#endif
|
Loading…
Reference in New Issue