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 DISTNAME = crap
VERSION = git 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 EXE = design
SHOBJ = crap_eq.so crap_eq_const.so crap_noise.so HEADERS = crap_util.h crap_util_def.h
MID = SHOBJ = ${PLUGINS:=.so}
HEADERS = crap_util.h crap_util_def.h ladspa.h 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 BENCH = bench.o
AGAINST = ./crap_eq_const.so AGAINST = ./crap_eq_const-ladspa.so
OBJ = ${SHOBJ:.so=.o} ${EXE:=.o} ${MID} VST_SDK_DIR ?= .
SRC = ${OBJ:.o=.c} 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 = -Wall -Wno-unused-function ${CFLAGS} -std=gnu99
ALL_CFLAGS = -std=gnu99 -fpic ${WARNING_FLAGS} ${CFLAGS} ALL_CXXFLAGS = -Wno-write-strings ${CXXFLAGS} -I ${VST_SDK_DIR} -DBUILDING_DLL=1
ALL_LDFLAGS = -lm ${LDFLAGS} ALL_LDFLAGS = -lm ${LDFLAGS}
PREFIX ?= /usr/local PREFIX ?= /usr/local
@ -21,55 +39,94 @@ EXEC_PREFIX ?= ${PREFIX}
LIBDIR ?= ${EXEC_PREFIX}/lib LIBDIR ?= ${EXEC_PREFIX}/lib
LADSPADIR ?= ${LIBDIR}/ladspa LADSPADIR ?= ${LIBDIR}/ladspa
FULLNAME = ${DISTNAME}-${VERSION}
ALL = ${OBJ} ${SHOBJ} ${EXE}
LADSPADEST = ${DESTDIR}${LADSPADIR} 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} all: options ladspa vst ${EXE}
@echo LD ${BENCH} ${MID} -o $@
.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 @${CC} ${ALL_CFLAGS} ${BENCH} -o $@ ${ALL_LDFLAGS} -rdynamic -ldl
.PHONY: benchmark .PHONY: benchmark
benchmark: bench benchmark: bench
./benchtime ./bench ${AGAINST} ./benchtime ./bench ${AGAINST}
.PHONY: options vstsdk.o: ${VST_SRC}
options: @echo CXX -c $^
@echo "ALL_CFLAGS = ${ALL_CFLAGS}" @${CXX} -c ${ALL_CXXFLAGS} ${CPPFLAGS} $^
@echo "CPPFLAGS = ${CPPFLAGS}" @echo LD -r ${VST_OBJ} -o $@
@echo "ALL_LDFLAGS = ${ALL_LDFLAGS}" @${LD} -r ${VST_OBJ} -o $@
@echo "CC = ${CC}" rm ${VST_OBJ}
%.so: %.o ${MID} %-ladspa.so: %-ladspa.o
@echo LD $< ${MID} -o $@ @echo CC $^ -o $@
@${CC} ${ALL_CFLAGS} -shared $< ${MID} -o $@ ${ALL_LDFLAGS} @${CC} ${ALL_CFLAGS} -shared $^ -o $@ ${ALL_LDFLAGS}
%: %.o ${MID} %-vst.so: %-vst.o vstsdk.o
@echo LD $< ${MID} -o $@ @echo CXX $^ -o $@
@${CC} ${ALL_CFLAGS} $< ${MID} -o $@ ${ALL_LDFLAGS} @${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 $@ @echo CC $< -o $@
@${CC} ${ALL_CFLAGS} $< -o $@ ${ALL_LDFLAGS}
%.o: %.c
@echo CC -c $< -o $@
@${CC} -c ${ALL_CFLAGS} ${CPPFLAGS} $< -o $@ @${CC} -c ${ALL_CFLAGS} ${CPPFLAGS} $< -o $@
install: all install: all
mkdir -p ${LADSPADEST} mkdir -p ${LADSPADEST}
install -d ${LADSPADEST} install -d ${LADSPADEST}
install -m 644 ${SHOBJ} ${LADSPADEST} install -m 644 ${LADSPA:=.so} ${LADSPADEST}
.PHONY: clean .PHONY: clean
clean: clean:
-rm -f ${ALL} bench ${BENCH} -rm -f ${ALL} ${MISC_CLEAN}
.PHONY: dist .PHONY: dist
dist: dist:
-rm -f ${FULLNAME}.tar.gz -rm -f ${FULLNAME}.tar.gz
mkdir -p ${FULLNAME} mkdir ${FULLNAME}
cp LICENSE README.md Makefile ${HEADERS} ${SRC} ${FULLNAME} cp ${MISC_DIST} ${HEADERS} ${SRC} ${FULLNAME}
cp bench.sh ${BENCH:.o=.c} ${FULLNAME}
tar -cf ${FULLNAME}.tar ${FULLNAME} tar -cf ${FULLNAME}.tar ${FULLNAME}
gzip ${FULLNAME}.tar gzip ${FULLNAME}.tar
rm -rf ${FULLNAME} rm -r ${FULLNAME}

View file

@ -1,21 +1,27 @@
# crap # 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 ## plugs
### crap_eq ### crap Parametric Equalizer
__crap_eq (0x000CAFED)__
multiband parametric EQ. try redefining BANDS. multiband parametric EQ. try redefining BANDS.
### crap_eq_const ### crap const Equalizer
simplified code for a static response. __crap_eq_const (0x0DEFACED)__
one day, this will be faster with compile-time constants.
### 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. 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. `make` it. optional `benchmark` target which doesn't build on Windows.
if you're using gcc, try `CFLAGS='-O3 -ffast-math'`. with gcc, try `CFLAGS='-O3 -ffast-math'`.
`-march=native` actually seems to degrade performance slightly, but YMMV. `-march=native` seems to degrade performance slightly, but YMMV.
if you're using clang, (at the time of writing this) with clang, (at the time of writing this)
you can't tune performance that much so just use `CFLAGS='-O2'`. optimizations are not as tuneable so try `CFLAGS='-O2'`.
can be cross-compiled for Windows for use in Audacity or a LADSPA wrapper. 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;
}