plugin template system; vst support

i need a beer
This commit is contained in:
Connor Olding 2013-11-17 19:32:45 -08:00
parent d609b1f687
commit eb841cbd4a
11 changed files with 485 additions and 157 deletions

121
Makefile
View file

@ -1,19 +1,37 @@
# NOTE: C implies LADSPA, C++ implies VST
# it's hackish but it'll do for now
DISTNAME = crap
VERSION = git
FULLNAME = ${DISTNAME}-${VERSION}
BOTH = crap_eq_const
LADSPA_ONLY = crap_eq crap_noise
VST_ONLY =
LADSPA = ${BOTH:=-ladspa} ${LADSPA_ONLY:=-ladspa}
VST = ${BOTH:=-vst} ${VST_ONLY:=-vst}
PLUGINS = ${LADSPA} ${VST}
EXE = design
SHOBJ = crap_eq.so crap_eq_const.so crap_noise.so
MID =
HEADERS = crap_util.h crap_util_def.h ladspa.h
HEADERS = crap_util.h crap_util_def.h
SHOBJ = ${PLUGINS:=.so}
OBJ = ${PLUGINS:=.o} vstsdk.o
# only for dist target right now
SRC = ${BOTH:=.h} ${LADSPA_ONLY:=-ladspa.c} ${VST_ONLY:=-vst.cpp} ${EXE:=.c}
BENCH = bench.o
AGAINST = ./crap_eq_const.so
AGAINST = ./crap_eq_const-ladspa.so
OBJ = ${SHOBJ:.so=.o} ${EXE:=.o} ${MID}
SRC = ${OBJ:.o=.c}
VST_SDK_DIR ?= .
VST_CPP = audioeffect.cpp audioeffectx.cpp vstplugmain.cpp
VST_SRC = ${VST_CPP:%=${VST_SDK_DIR}/public.sdk/source/vst2.x/%}
# temp objects before combining to vstsdk.o
VST_OBJ = ${VST_CPP:.cpp=.o}
VST_DEF = ${VST_SDK_DIR}/public.sdk/samples/vst2.x/win/vstplug.def
WARNING_FLAGS = -Wall -Wno-unused-function
ALL_CFLAGS = -std=gnu99 -fpic ${WARNING_FLAGS} ${CFLAGS}
ALL_CFLAGS = -Wall -Wno-unused-function ${CFLAGS} -std=gnu99
ALL_CXXFLAGS = -Wno-write-strings ${CXXFLAGS} -I ${VST_SDK_DIR} -DBUILDING_DLL=1
ALL_LDFLAGS = -lm ${LDFLAGS}
PREFIX ?= /usr/local
@ -21,55 +39,94 @@ EXEC_PREFIX ?= ${PREFIX}
LIBDIR ?= ${EXEC_PREFIX}/lib
LADSPADIR ?= ${LIBDIR}/ladspa
FULLNAME = ${DISTNAME}-${VERSION}
ALL = ${OBJ} ${SHOBJ} ${EXE}
LADSPADEST = ${DESTDIR}${LADSPADIR}
all: options ${ALL}
ALL = ${SHOBJ} ${OBJ} ${EXE}
MISC_CLEAN = bench ${BENCH}
MISC_DIST = LICENSE README.md Makefile
MISC_DIST += benchtime ${BENCH:.o=.c}
MISC_DIST += generate-ladspa generate-vst common.sh
MISC_DIST += template-vst.cpp template-ladspa.c ladspa.h
bench: all ${BENCH}
@echo LD ${BENCH} ${MID} -o $@
all: options ladspa vst ${EXE}
.PHONY: options
options:
@echo "CPPFLAGS = ${CPPFLAGS}"
@echo "ALL_CFLAGS = ${ALL_CFLAGS}"
@echo "ALL_CXXFLAGS = ${ALL_CXXFLAGS}"
@echo "ALL_LDFLAGS = ${ALL_LDFLAGS}"
@echo "CC = ${CC}"
@echo "CXX = ${CXX}"
@echo "LD = ${LD}"
@echo
ladspa: ${LADSPA:=.so}
vst: ${VST:=.so}
bench: ${AGAINST} ${BENCH}
@echo CC ${BENCH} -o $@
@${CC} ${ALL_CFLAGS} ${BENCH} -o $@ ${ALL_LDFLAGS} -rdynamic -ldl
.PHONY: benchmark
benchmark: bench
./benchtime ./bench ${AGAINST}
.PHONY: options
options:
@echo "ALL_CFLAGS = ${ALL_CFLAGS}"
@echo "CPPFLAGS = ${CPPFLAGS}"
@echo "ALL_LDFLAGS = ${ALL_LDFLAGS}"
@echo "CC = ${CC}"
vstsdk.o: ${VST_SRC}
@echo CXX -c $^
@${CXX} -c ${ALL_CXXFLAGS} ${CPPFLAGS} $^
@echo LD -r ${VST_OBJ} -o $@
@${LD} -r ${VST_OBJ} -o $@
rm ${VST_OBJ}
%.so: %.o ${MID}
@echo LD $< ${MID} -o $@
@${CC} ${ALL_CFLAGS} -shared $< ${MID} -o $@ ${ALL_LDFLAGS}
%-ladspa.so: %-ladspa.o
@echo CC $^ -o $@
@${CC} ${ALL_CFLAGS} -shared $^ -o $@ ${ALL_LDFLAGS}
%: %.o ${MID}
@echo LD $< ${MID} -o $@
@${CC} ${ALL_CFLAGS} $< ${MID} -o $@ ${ALL_LDFLAGS}
%-vst.so: %-vst.o vstsdk.o
@echo CXX $^ -o $@
@${CXX} ${ALL_CXXFLAGS} -shared $^ -o $@ ${ALL_LDFLAGS}
%.o: %.c ${HEADERS}
%-ladspa.o: %-ladspa.c ${HEADERS} ladspa.h
@echo CC -c $< -o $@
@${CC} -c ${ALL_CFLAGS} ${CPPFLAGS} $< -o $@
%-vst.o: %-vst.cpp ${HEADERS}
@echo CXX -c $< -o $@
@${CXX} -c ${ALL_CXXFLAGS} ${CPPFLAGS} $< -o $@
%-ladspa.c: %.h template-ladspa.c generate-ladspa common.sh
./generate-ladspa $< $@
%-vst.cpp: %.h template-vst.cpp generate-vst common.sh
./generate-vst $< $@
.SUFFIXES:
%: %.o
@echo CC $< -o $@
@${CC} ${ALL_CFLAGS} $< -o $@ ${ALL_LDFLAGS}
%.o: %.c
@echo CC -c $< -o $@
@${CC} -c ${ALL_CFLAGS} ${CPPFLAGS} $< -o $@
install: all
mkdir -p ${LADSPADEST}
install -d ${LADSPADEST}
install -m 644 ${SHOBJ} ${LADSPADEST}
install -m 644 ${LADSPA:=.so} ${LADSPADEST}
.PHONY: clean
clean:
-rm -f ${ALL} bench ${BENCH}
-rm -f ${ALL} ${MISC_CLEAN}
.PHONY: dist
dist:
-rm -f ${FULLNAME}.tar.gz
mkdir -p ${FULLNAME}
cp LICENSE README.md Makefile ${HEADERS} ${SRC} ${FULLNAME}
cp bench.sh ${BENCH:.o=.c} ${FULLNAME}
mkdir ${FULLNAME}
cp ${MISC_DIST} ${HEADERS} ${SRC} ${FULLNAME}
tar -cf ${FULLNAME}.tar ${FULLNAME}
gzip ${FULLNAME}.tar
rm -rf ${FULLNAME}
rm -r ${FULLNAME}

View file

@ -1,21 +1,27 @@
# crap
connor's rancid audio plugins. LADSPA.
connor's rancid audio plugins. LADSPA and VST.
alternatively, chocolate recipes are powerful.
alternatively, configuration realizes any personality
## plugs
### crap_eq
### crap Parametric Equalizer
__crap_eq (0x000CAFED)__
multiband parametric EQ. try redefining BANDS.
### crap_eq_const
### crap const Equalizer
simplified code for a static response.
one day, this will be faster with compile-time constants.
__crap_eq_const (0x0DEFACED)__
### crap_noise
simpler code with a static response.
edit code as needed.
### crap noises generator
__crap_noise (0xEC57A71C)__
white noise generator. loud, full-range, 0dBFS. don't say i didn't warn you.
@ -23,10 +29,10 @@ white noise generator. loud, full-range, 0dBFS. don't say i didn't warn you.
`make` it. optional `benchmark` target which doesn't build on Windows.
if you're using gcc, try `CFLAGS='-O3 -ffast-math'`.
`-march=native` actually seems to degrade performance slightly, but YMMV.
with gcc, try `CFLAGS='-O3 -ffast-math'`.
`-march=native` seems to degrade performance slightly, but YMMV.
if you're using clang, (at the time of writing this)
you can't tune performance that much so just use `CFLAGS='-O2'`.
with clang, (at the time of writing this)
optimizations are not as tuneable so try `CFLAGS='-O2'`.
can be cross-compiled for Windows for use in Audacity or a LADSPA wrapper.

12
common.sh Normal file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -e
set -u
p="$1"
out="$2"
while read -r; do
:
done < "$p"
pp_include="#include \"$p\""

View file

@ -1,114 +0,0 @@
#include <stdlib.h>
#include <math.h>
#include "ladspa.h"
#include "crap_util.h"
typedef unsigned long ulong;
#define REALBANDS 4
#define EQ_INPUT 0
#define EQ_OUTPUT 1
#define PCOUNT 2
const LADSPA_PortDescriptor p_discs[PCOUNT] = {
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO
};
const LADSPA_PortRangeHint p_hints[PCOUNT] = {
{.HintDescriptor = 0},
{.HintDescriptor = 0}
};
const char *p_names[PCOUNT] = {"Input", "Output"};
typedef struct {
LADSPA_Data *input;
LADSPA_Data *output;
biquad filters[REALBANDS];
LADSPA_Data fs;
} eq_t;
static void
activate_eq(LADSPA_Handle instance) {
eq_t *eq = (eq_t *)instance;
biquad *filters = eq->filters;
for (int i = 0; i < REALBANDS; i++)
biquad_init(&filters[i]);
}
static void
cleanup_eq(LADSPA_Handle instance) {
free(instance);
}
static void
connect_port_eq(LADSPA_Handle instance, ulong port, LADSPA_Data *data) {
eq_t *eq = (eq_t *)instance;
if (port == EQ_INPUT)
eq->input = data;
else if (port == EQ_OUTPUT)
eq->output = data;
}
static LADSPA_Handle
instantiate_eq(const LADSPA_Descriptor *descriptor, ulong s_rate) {
eq_t *eq = (eq_t *) calloc(1, sizeof(eq_t));
biquad *filters = eq->filters;
LADSPA_Data fs = s_rate;
eq->fs = fs;
filters[0] = biquad_gen(4, 10, 0.0, 1.00, fs);
filters[1] = biquad_gen(0, 36, 4.3, 1.25, fs);
filters[2] = biquad_gen(2, 1400, -7.0, 1.20, fs);
filters[3] = biquad_gen(0, 7000, 3.3, 1.25, fs);
return (LADSPA_Handle) eq;
}
static void
run_eq(LADSPA_Handle instance, ulong sample_count) {
eq_t *eq = (eq_t *) instance;
biquad *filters = eq->filters;
const LADSPA_Data *input = eq->input;
LADSPA_Data *output = eq->output;
for (ulong pos = 0; pos < sample_count; pos++) {
LADSPA_Data samp = input[pos];
for (int i = 0; i < REALBANDS; i++)
samp = biquad_run(&filters[i], samp);
output[pos] = samp;
}
}
static const LADSPA_Descriptor eqDescriptor = {
.UniqueID = 0xDEFACED,
.Label = "crap_eq_const",
.Properties = 0,
.Name = "crap const Equalizer",
.Maker = "Connor Olding",
.Copyright = "MIT",
.PortCount = PCOUNT,
.PortDescriptors = p_discs,
.PortRangeHints = p_hints,
.PortNames = p_names,
.activate = activate_eq,
.cleanup = cleanup_eq,
.connect_port = connect_port_eq,
.deactivate = NULL,
.instantiate = instantiate_eq,
.run = run_eq,
.run_adding = NULL,
.set_run_adding_gain = NULL
};
const LADSPA_Descriptor *
ladspa_descriptor(ulong index) {
if (index != 0)
return NULL;
return &eqDescriptor;
}

76
crap_eq_const.h Normal file
View file

@ -0,0 +1,76 @@
#include <string.h>
#define BIQUAD_DOUBLE
#include "crap_util.h"
#define ID 0x0DEFACED
#define LABEL "crap_eq_const"
#define NAME "crap Constant Equalizer"
#define AUTHOR "Connor Olding"
#define COPYRIGHT "MIT"
#define PARAMETERS 0
#define BANDS 4
typedef struct {
biquad filters[2][BANDS];
} personal;
static bq_t
process_one(biquad *filters, bq_t samp)
{
for (int i = 0; i < BANDS; i++)
samp = biquad_run(&filters[i], samp);
return samp*1.2023;
}
static void
process(personal *data,
float *in_L, float *in_R,
float *out_L, float *out_R,
unsigned long count) {
for (unsigned long pos = 0; pos < count; pos++) {
out_L[pos] = process_one(data->filters[0], in_L[pos]);
out_R[pos] = process_one(data->filters[1], in_R[pos]);
}
}
static void
process_double(personal *data,
double *in_L, double *in_R,
double *out_L, double *out_R,
unsigned long count) {
// TODO: test which hosts use this
for (unsigned long pos = 0; pos < count; pos++) {
out_L[pos] = process_one(data->filters[0], in_L[pos]);
out_R[pos] = process_one(data->filters[1], in_R[pos]);
}
}
static void
construct(personal *data) {
}
static void
destruct(personal *data) {
}
static void
resume(personal *data) {
biquad *filters = data->filters[0];
for (int i = 0; i < BANDS; i++)
biquad_init(&filters[i]);
memcpy(data->filters[1], filters, BANDS*sizeof(biquad));
}
static void
pause(personal *data) {
}
static void
adjust(personal *data, unsigned long fs) {
biquad *filters = data->filters[0];
filters[0] = biquad_gen(0, 34.34, +4.6, 1.21, fs);
filters[1] = biquad_gen(0, 85.74, -1.2, 1.31, fs);
filters[2] = biquad_gen(2, 862.2, -5.5, 1.00, fs);
filters[3] = biquad_gen(0, 7496., +3.3, 1.10, fs);
}

18
generate-ladspa Executable file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
. common.sh
(cat template-ladspa.c | while read -r; do
case "$REPLY" in
'//#INCLUDE') echo "$pp_include" ;;
# unimplemented
'//#PORT_DESCRIPTIONS') :;;
'//#PORT_HINTS') :;;
'//#PORT_NAMES') :;;
'//#PORT_DATA') :;;
'//#PORT_CONNECT') :;;
*) echo -E "$REPLY" ;;
esac
done) > "$out"

14
generate-vst Executable file
View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
. common.sh
(cat template-vst.cpp | while read -r; do
case "$REPLY" in
'//#INCLUDE') echo "$pp_include" ;;
# unimplemented
'//#VST_PARAMS') :;;
*) echo -E "$REPLY" ;;
esac
done) > "$out"

132
template-ladspa.c Normal file
View file

@ -0,0 +1,132 @@
#include <stdlib.h>
#include "ladspa.h"
//#INCLUDE
#define PLUG_INPUT_L 0
#define PLUG_INPUT_R 1
#define PLUG_OUTPUT_L 2
#define PLUG_OUTPUT_R 3
#define PCOUNT (PARAMETERS + 4)
const LADSPA_PortDescriptor p_discs[PCOUNT] = {
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO
,
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO
,
LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO
,
LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO
//#PORT_DESCRIPTIONS
};
const LADSPA_PortRangeHint p_hints[PCOUNT] = {
{.HintDescriptor = 0}
,
{.HintDescriptor = 0}
,
{.HintDescriptor = 0}
,
{.HintDescriptor = 0}
//#PORT_HINTS
};
const char *p_names[PCOUNT] = {
"L input"
,
"R input"
,
"L output"
,
"R output"
//#PORT_NAMES
};
typedef struct {
LADSPA_Data *input_L;
LADSPA_Data *input_R;
LADSPA_Data *output_L;
LADSPA_Data *output_R;
//#PORT_DATA
personal data;
} plug_t;
static void
plug_connect(LADSPA_Handle instance, unsigned long port, LADSPA_Data *data) {
plug_t *plug = (plug_t *)instance;
if (port == PLUG_INPUT_L)
plug->input_L = data;
else if (port == PLUG_INPUT_R)
plug->input_R = data;
else if (port == PLUG_OUTPUT_L)
plug->output_L = data;
else if (port == PLUG_OUTPUT_R)
plug->output_R = data;
//#PORT_CONNECT
}
static void
plug_resume(LADSPA_Handle instance) {
plug_t *plug = (plug_t *)instance;
resume(&plug->data);
}
static void
plug_pause(LADSPA_Handle instance) {
plug_t *plug = (plug_t *)instance;
pause(&plug->data);
}
static LADSPA_Handle
plug_construct(const LADSPA_Descriptor *descriptor, unsigned long fs) {
plug_t *plug = (plug_t *) calloc(1, sizeof(plug_t));
construct(&plug->data);
adjust(&plug->data, fs);
return (LADSPA_Handle) plug;
}
static void
plug_destruct(LADSPA_Handle instance) {
plug_t *plug = (plug_t *)instance;
destruct(&plug->data);
free(plug);
}
static void
plug_process(LADSPA_Handle instance, unsigned long count) {
plug_t *plug = (plug_t *)instance;
process(&plug->data,
plug->input_L, plug->input_R,
plug->output_L, plug->output_R,
count);
}
static const LADSPA_Descriptor plug_desc = {
.UniqueID = ID,
.Label = LABEL,
.Properties = 0,
.Name = NAME,
.Maker = AUTHOR,
.Copyright = COPYRIGHT,
.PortCount = PCOUNT,
.PortDescriptors = p_discs,
.PortRangeHints = p_hints,
.PortNames = p_names,
.instantiate = plug_construct,
.cleanup = plug_destruct,
.activate = plug_resume,
.deactivate = plug_pause,
.connect_port = plug_connect,
.run = plug_process,
.run_adding = NULL,
.set_run_adding_gain = NULL
};
const LADSPA_Descriptor *
ladspa_descriptor(unsigned long index) {
if (index != 0)
return NULL;
return &plug_desc;
}

127
template-vst.cpp Normal file
View file

@ -0,0 +1,127 @@
#include "public.sdk/source/vst2.x/audioeffectx.h"
//#include "vstparam.h"
//#INCLUDE
class plugin : public AudioEffectX
{
public:
plugin(audioMasterCallback audioMaster);
~plugin();
void resume();
void suspend();
void processReplacing(float **, float **, VstInt32);
void processDoubleReplacing(double **, double **, VstInt32);
bool setProcessPrecision(VstInt32 precision);
/*
void setParameter(VstInt32, float);
float getParameter(VstInt32);
void getParameterLabel(VstInt32, char *);
void getParameterDisplay(VstInt32, char *);
void getParameterName(VstInt32, char *);
*/
void setSampleRate(float);
bool getEffectName(char *);
bool getVendorString(char *);
bool getProductString(char *);
//VstInt32 getVendorVersion();
private:
#if (PARAMETERS > 0)
VstParam *m_params[PARAMETERS];
#endif
personal data;
};
AudioEffect *
createEffectInstance(audioMasterCallback audioMaster) {
return new plugin(audioMaster);
}
plugin::plugin(audioMasterCallback audioMaster)
: AudioEffectX(audioMaster, 1, PARAMETERS)
{
//#VST_PARAMS
//m_params[n] = new VstParam("Input", "dB", -12.0, 12.0, 0.0, NULL, NULL, 1, NULL);
setNumInputs(2);
setNumOutputs(2);
setUniqueID(ID);
canProcessReplacing();
::construct(&data);
}
plugin::~plugin()
{
::destruct(&data);
//for (int i = 0; i < PARAMETERS; i++)
// delete m_params[i];
}
void
plugin::resume() {
::resume(&data);
AudioEffectX::resume();
}
void
plugin::suspend() {
AudioEffectX::suspend();
::pause(&data);
}
void
plugin::processReplacing(float **inputs, float **outputs, VstInt32 count) {
::process(&data,
inputs[0], inputs[1],
outputs[0], outputs[1],
count);
}
void
plugin::processDoubleReplacing(double **inputs, double **outputs, VstInt32 count) {
::process_double(&data,
inputs[0], inputs[1],
outputs[0], outputs[1],
count);
}
bool
plugin::setProcessPrecision(VstInt32 precision) {
return true;
}
/*
parameter funcs go here
if (index >= PARAMETERS) return;
*/
void
plugin::setSampleRate(float fs) {
AudioEffectX::setSampleRate(fs);
::adjust(&data, (unsigned long) fs);
}
bool
plugin::getEffectName(char *name) {
vst_strncpy(name, LABEL, kVstMaxEffectNameLen);
return true;
}
bool
plugin::getProductString(char *text)
{
vst_strncpy(text, NAME, kVstMaxProductStrLen);
return true;
}
bool
plugin::getVendorString(char *text)
{
vst_strncpy(text, AUTHOR, kVstMaxVendorStrLen);
return true;
}