From de368d0ee3521f9c1ca3a76806c937161a35c1fc Mon Sep 17 00:00:00 2001 From: "Rachel Fae Fox (foxiepaws)" Date: Wed, 30 Oct 2019 20:09:28 -0400 Subject: [PATCH] Initial commit --- .gitignore | 3 ++ Makefile | 50 ++++++++++++++++++ src/common.h | 20 ++++++++ src/filters/svf.c | 83 ++++++++++++++++++++++++++++++ src/filters/svf.h | 42 +++++++++++++++ src/synths/basic.c | 48 +++++++++++++++++ src/synths/basic.h | 30 +++++++++++ src/synththing.c | 120 +++++++++++++++++++++++++++++++++++++++++++ src/utils/envelope.c | 43 ++++++++++++++++ src/utils/envelope.h | 35 +++++++++++++ 10 files changed, 474 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 src/common.h create mode 100644 src/filters/svf.c create mode 100644 src/filters/svf.h create mode 100644 src/synths/basic.c create mode 100644 src/synths/basic.h create mode 100644 src/synththing.c create mode 100644 src/utils/envelope.c create mode 100644 src/utils/envelope.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20f3526 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +bin/ +*.o +*.gch diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f4a149b --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ + +CC ?= gcc +SRCDIR ?= src +INCDIR += -I${SRCDIR} +OBJDIR ?= src +BINDIR ?= bin +INSTALLDIR ?= /usr/local/bin + +TARGET ?= synththing + +CFLAGS += -std=c99 +CFLAGS += `pkg-config --cflags portaudio-2.0` +LDFLAGS += `pkg-config --libs portaudio-2.0` + +UNAME=$(shell uname -s) +ifeq (${UNAME},FreeBSD) +INCDIR += -I/sys/ +endif + +SHELL=/bin/sh +SRC = $(wildcard ${SRCDIR}/*.c ${SRCDIR}/*/*.c) +OBJ = ${SRC:${SRCDIR}/%.c=${OBJDIR}/%.o} + + +all: synththing + +${OBJ}: $(OBJDIR)/%.o : $(SRCDIR)/%.c + @echo CC $< + ${CC} -o $@ ${INCDIR} -c ${CFLAGS} ${DEFINES} $< + +synththing: ${OBJ} + @echo CC -o ${BINDIR}/${TARGET} + @mkdir -p ${BINDIR} + ${CC} -I${INCDIR} -o ${BINDIR}/${TARGET} ${OBJ} ${LDFLAGS} ${CFLAGS} ${DEFINES} + +clean: + @echo cleaning + @rm -f syswriter ${OBJ} + +remove: clean + @echo deleting binary + @rm -f ${BINDIR}/${TARGET} + @rm -r ${BINDIR} + +install: synththing + mkdir -p ${INSTALLDIR} + cp ${BINDIR}/${TARGET} ${INSTALLDIR}/${TARGET} + chmod 4555 ${INSTALLDIR}/${TARGET} + + diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..0c4144b --- /dev/null +++ b/src/common.h @@ -0,0 +1,20 @@ +/* + * Filename: common.h + * + * Description: + * + * + * Version: + * Created: Wed Oct 30 17:12:37 2019 + * Revision: None + * Author: Rachel Fae Fox (foxiepaws),fox@foxiepa.ws + * + */ +#ifndef _H_COMMON +#define _H_COMMON +#include +typedef struct EngineState { + uint32_t t; + int sample_rate; +} EngineState; +#endif diff --git a/src/filters/svf.c b/src/filters/svf.c new file mode 100644 index 0000000..fec5a7c --- /dev/null +++ b/src/filters/svf.c @@ -0,0 +1,83 @@ +/* + * Filename: svf.c + * + * Description: + * + * + * Version: + * Created: Wed Oct 30 00:00:40 2019 + * Revision: None + * Author: Rachel Fae Fox (foxiepaws),fox@foxiepa.ws + * + */ + +#include +#include +#include +#define M_PI 3.14159265358979323846 +#define MAX(a,b) (a>b?a:b) +#define MIN(a,b) (afreq/(44100*2))); + return MIN(2.0 * (1.0 - powf(self->q,0.25)), MIN(2.0, 2.0/freq - freq * 0.5)); + // return MIN(2.0 * (1.0 - pow(self->q,0.25)), MIN(2.0, (2.0/freq - freq * 0.5))); + +} +float svf_process(SVF *self,float in) { + float a = in; + float freq = 2.0*sin(M_PI * MIN(0.25, self->freq/(44100*2))); + float damp = self->damp(self); + //printf("prefilt status: freq: %f, drive: %f, damp: %f, in: %f, low: %f, notch: %f, high: %f, band: %f\n",self->freq,self->drive, damp,in, self->low, self->notch, self->high, self->band); + for (unsigned int i = 0; i <= self->dbo; i++) { + self->notch = a - damp * self->band; + self->low = self->low + freq * self->band; + self->high = self->notch - self->low; + self->band = freq * self->high + self->band - self->drive * self->band * self->band * self->band; + a = self->low; + } + self->out = a; + //printf("postfilt status: freq: %f, drive: %f, damp: %f, in: %f, low: %f, notch: %f, high: %f, band: %f\n",self->freq,self->drive, damp,in, self->low, self->notch, self->high, self->band); + return self->out; +} + +void svf_reset(SVF* self){ + self->notch = 0; + self->low = 0; + self->band = 0; + self->high = 0; + self->out = 0; +} + +SVF* svf_new(float freq,float q,float drive) { + SVF *self = malloc(sizeof(SVF)); + + self->freq = freq; + self->q = q; + self->drive = drive; + self->dbo = _12dbo; + // functions. + self->damp = svf_damp; + self->process = svf_process; + self->reset = svf_reset; + self->reset(self); + return self; +} diff --git a/src/filters/svf.h b/src/filters/svf.h new file mode 100644 index 0000000..65c1ced --- /dev/null +++ b/src/filters/svf.h @@ -0,0 +1,42 @@ +/* + * Filename: svf.c + * + * Description: + * + * + * Version: + * Created: Wed Oct 30 00:00:40 2019 + * Revision: None + * Author: Rachel Fae Fox (foxiepaws),fox@foxiepa.ws + * + */ +#ifndef _H_FILTERS_SVF +#define _H_FILTERS_SVF +#include +#include + +#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) + +typedef enum DBO { _6dbo, _12dbo, _18dbo, _24dbo, _36dbo = 6, _48dbo } DBO; + +typedef struct SVF { + float notch; + float low; + float high; + float band; + float drive; + float freq; + float q; + DBO dbo; + float out; + float(*damp)(struct SVF*); + float(*process)(struct SVF*,float in); + void(*reset)(struct SVF*); +} SVF; + + +float svf_damp(SVF *self); +float svf_process(SVF *self,float in); +void svf_reset(SVF* self); +SVF* svf_new(float freq,float q,float drive); +#endif diff --git a/src/synths/basic.c b/src/synths/basic.c new file mode 100644 index 0000000..439a30a --- /dev/null +++ b/src/synths/basic.c @@ -0,0 +1,48 @@ +/* + * Filename: basic.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 "../utils/envelope.h" +#include +#include +typedef enum Waveform {_waveform_sine, _waveform_saw} Waveform; +typedef struct Basic { + float freq; + Waveform shape; + int gate; + Envelope Env; + float(*process)(struct Basic *, EngineState*); +} Basic; + + +float basic_process(Basic *self, EngineState *t) { + float out; + switch (self->shape) { + case _waveform_saw: + out = fmod((self->freq+1) * t->t / 44100,1.0); + break; + default: + out = 0.0F; + break; + } + + return out; +} + +Basic* basic_new(Waveform w, Envelope e) { + Basic *b = malloc(sizeof(Envelope)); + b->shape = w; + b->process = basic_process; + return b; +} + diff --git a/src/synths/basic.h b/src/synths/basic.h new file mode 100644 index 0000000..3ddf482 --- /dev/null +++ b/src/synths/basic.h @@ -0,0 +1,30 @@ +/* + * Filename: basic.h + * + * Description: + * + * + * Version: + * Created: Wed Oct 30 17:37:08 2019 + * Revision: None + * Author: Rachel Fae Fox (foxiepaws),fox@foxiepa.ws + * + */ +#include "../common.h" +#include "../utils/envelope.h" + +#ifndef _H_BASIC +#define _H_BASIC + +typedef enum Waveform {_waveform_sine, _waveform_saw} Waveform; +typedef struct Basic { + float freq; + Waveform shape; + int gate; + Envelope Env; + float(*process)(struct Basic *, EngineState*); +} Basic; + +float basic_process(Basic *self, EngineState *t); +Basic* basic_new(Waveform w, Envelope e); +#endif diff --git a/src/synththing.c b/src/synththing.c new file mode 100644 index 0000000..588e9b3 --- /dev/null +++ b/src/synththing.c @@ -0,0 +1,120 @@ +/* + * Filename: synththing.c + * + * Description: + * + * + * Version: + * Created: Wed Oct 30 17:52:12 2019 + * Revision: None + * Author: Rachel Fae Fox (foxiepaws),fox@foxiepa.ws + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "synths/basic.h" +#include "filters/svf.h" +#include "utils/envelope.h" + + + +#define SAMPLE_RATE (44100) + + + +Basic saw; +SVF filt; +static EngineState data; + +int mycallback( const void *input, + void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) { + + EngineState *data = (EngineState*)userData; + float *out = (float*) output; + unsigned int i; + + for( i=0; it++; + } + return 0; +} + +int main(int argc, char**argv) { + + /* portaudio boilerplate crap */ + PaError err; + err = Pa_Initialize(); + const PaDeviceInfo *deviceInfo; + int numDevices; + numDevices = Pa_GetDeviceCount(); + if( numDevices < 0 ) + { + printf( "ERROR: Pa_CountDevices returned 0x%x\n", numDevices ); + err = numDevices; + goto error; + } + + for(int i=0; iname,deviceInfo->maxInputChannels,deviceInfo->maxOutputChannels,deviceInfo->defaultSampleRate); + } + + Envelope b = *envelope_new(); + saw = *basic_new(_waveform_saw, b); + saw.freq = 440; + filt = *svf_new(7000.0f, 0.99f, 0.000001f); + //data.sample_rate=SAMPLE_RATE; + PaStream *stream; + /* Open an audio I/O stream. */ + err = Pa_OpenDefaultStream( &stream, + 0, + 2, + paFloat32, + SAMPLE_RATE, + 256, + mycallback, + &data ); + + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + /* start anything intersting */ + saw.gate = true; + for (int freq = 7000; freq >= 60; freq--) { + filt.freq = freq; + usleep(500); + } + sleep(1); + + /* more crap */ + error: + printf( "PortAudio error: %s\n", Pa_GetErrorText( err ) ); + err = Pa_Terminate(); + if( err != paNoError ) + printf( "PortAudio error: %s\n", Pa_GetErrorText( err ) ); + +} diff --git a/src/utils/envelope.c b/src/utils/envelope.c new file mode 100644 index 0000000..398024a --- /dev/null +++ b/src/utils/envelope.c @@ -0,0 +1,43 @@ +/* + * Filename: envelope.h + * + * Description: + * + * + * Version: + * Created: Wed Oct 30 17:15:02 2019 + * Revision: None + * Author: Rachel Fae Fox (foxiepaws),fox@foxiepa.ws + * + */ +#include "../common.h" +#ifndef _H_ENVELOPE +#define _H_ENVELOPE +#include +#include + +typedef struct Envelope { + // these values are in samples. + unsigned int attack; + unsigned int decay; + unsigned int release; + float sustain_level; + float max_level; + float min_level; + bool gate; + float(*process)(struct Envelope *,EngineState *); + +} Envelope; + +float envelope_process(Envelope *self, EngineState *t) { + return self->gate ? self->max_level : self-> min_level; +} +Envelope* envelope_new() { + Envelope *e = malloc(sizeof(Envelope)); + e->process = envelope_process; + e->max_level = 1.0f; + e->min_level = 0.0f; + return e; +} + +#endif diff --git a/src/utils/envelope.h b/src/utils/envelope.h new file mode 100644 index 0000000..44b37aa --- /dev/null +++ b/src/utils/envelope.h @@ -0,0 +1,35 @@ +/* + * Filename: envelope.h + * + * Description: + * + * + * Version: + * Created: Wed Oct 30 17:15:02 2019 + * Revision: None + * Author: Rachel Fae Fox (foxiepaws),fox@foxiepa.ws + * + */ + +#ifndef _H_ENVELOPE +#define _H_ENVELOPE +#include +#include "../common.h" + +typedef struct Envelope { + // these values are in samples. + unsigned int attack; + unsigned int decay; + unsigned int release; + float sustain_level; + float max_level; + float min_level; + bool gate; + float(*process)(struct Envelope *, EngineState *); + +} Envelope; + +float envelope_process(Envelope *self, EngineState *t); +Envelope* envelope_new(); + +#endif