slowly covering lv2 core

This commit is contained in:
Stefano D'Angelo 2024-01-09 11:47:25 +01:00
parent 520b1ff9cf
commit 8d577e7e46
19 changed files with 141 additions and 104 deletions

26
notes Normal file
View File

@ -0,0 +1,26 @@
company {
name:
VST3: PFactoryInfo.vendor
LV2: not used
url:
VST3: PFactoryInfo.url
LV2: not used
email:
VST3: PFactoryInfo.email
LV2: not used
}
product {
name:
VST3: PClassInfo{,2,W}.name
LV2: manifest.ttl plugin doap:name
version:
VST3: PClassInfo{2,W}.version (first 3 numbers)
LV2: not used
buildVersion:
VST3: PClassInfo{2,W}.version (last number)
LV2: not used
bundleName:
VST3: plugin folder name, plugin .dll name, Info.plist
LV2: plugin folder name, plugin .dll name, manifest.ttl plugin lv2:binary
}

View File

@ -5,7 +5,7 @@ CFLAGS = -fPIC -Wall -Wpedantic -Wextra -Wno-unused-parameter
BUNDLE_DIR = ${BUNDLE_NAME}.lv2
SO_FILE := ${BUNDLE_NAME}.so
SO_FILE = ${BUNDLE_NAME}.so
all: build/${BUNDLE_DIR}/manifest.ttl build/${BUNDLE_DIR}/${SO_FILE}
@ -13,7 +13,7 @@ build/${BUNDLE_DIR}/manifest.ttl: data/manifest.ttl | build/${BUNDLE_DIR}
cp $^ $@
build/${BUNDLE_DIR}/${SO_FILE}: src/lv2.c | build/${BUNDLE_DIR}
${CC} $^ -o $@ ${CFLAGS} -shared
${CC} $^ -o $@ ${CFLAGS} ${CFLAGS_EXTRA} ${LIBS_EXTRA} -shared
build/${BUNDLE_DIR}:
mkdir -p $@
@ -21,4 +21,4 @@ build/${BUNDLE_DIR}:
clean:
rm -fr build
.PHONY: clean
.PHONY: all clean

View File

@ -1 +1,3 @@
BUNDLE_NAME := {{=it.product.bundleName}}
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.lv2_make && it.lv2_make.cflags ? it.lv2_make.cflags : ""}}
LIBS_EXTRA := {{=it.make && it.make.libs ? it.make.libs : ""}} {{=it.lv2_make && it.lv2_make.libs ? it.lv2_make.libs : ""}}

View File

@ -12,12 +12,17 @@
{{?}}
lv2:binary <{{=it.product.bundleName}}.so> ;
doap:name "{{=it.product.name}}" ;
lv2:minorVersion {{=/^([0-9]+)\./.exec(it.lv2.version)[1]}} ;
lv2:microVersion {{=/^[0-9]+\.([0-9]+)/.exec(it.lv2.version)[1]}} ;
lv2:optionalFeature lv2:hardRTCapable ;
lv2:port [
{{~it.tibia.lv2.ports :p:i}}
a {{?p.type == "control"}}lv2:ControlPort{{??}}{{?p.cv}}lv2:CVPort{{??}}lv2:AudioPort{{?}}{{?}} ,
{{?p.direction == "input"}}lv2:InputPort{{??}}lv2:OutputPort{{?}} ;
lv2:name "{{=p.name}}" ;
{{?"shortName" in p}}
lv2:shortName "{{=p.shortName.substring(0, 16)}}" ;
{{?}}
lv2:symbol "{{=p.symbol}}" ;
{{?"defaultValue" in p}}
lv2:default {{=p.defaultValue.toExponential()}} ;
@ -30,6 +35,18 @@
{{?}}
{{?p.sidechain}}
lv2:portProperty lv2:isSideChain ;
{{?}}
{{?p.isBypass}}
lv2:designation lv2:enabled ;
{{?}}
{{?p.isLatency}}
lv2:designation lv2:latency ;
{{?}}
{{?p.toggled}}
lv2:portProperty lv2:toggled ;
{{?}}
{{?p.optional}}
lv2:portProperty lv2:connectionOptional ;
{{?}}
lv2:index {{=i}}
{{?i < it.tibia.lv2.ports.length - 1}}

View File

@ -6,7 +6,24 @@
#define DATA_PRODUCT_CONTROL_OUTPUTS_N {{=it.product.parameters.filter(x => x.direction == "output").length}}
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
static float param_defaults[DATA_PRODUCT_CONTROL_INPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "control" && x.direction == "input") :p}}{{=p.defaultValue.toExponential()}}f, {{~}}
# define DATA_PARAM_BYPASS 1
# define DATA_PARAM_TOGGLED (1<<1)
static struct {
float min;
float max;
float def;
uint32_t flags;
} param_data[DATA_PRODUCT_CONTROL_INPUTS_N] = {
{{~it.tibia.lv2.ports.filter(x => x.type == "control" && x.direction == "input") :p}}
{
/* .min = */ {{=p.minimum.toExponential()}}f,
/* .max = */ {{=p.maximum.toExponential()}}f,
/* .def = */ {{=p.defaultValue.toExponential()}}f,
/* .flags = */ 0{{?p.isBypass}} | DATA_PARAM_BYPASS{{?}}{{?p.toggled}} | DATA_PARAM_TOGGLED{{?}}
},
{{~}}
};
#endif

View File

@ -26,6 +26,14 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
return NULL;
plugin_init(&instance->p);
plugin_set_sample_rate(&instance->p, sample_rate);
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
for (size_t i = 0; i < DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N; i++)
instance->x[i] = NULL;
#endif
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
for (size_t i = 0; i < DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N; i++)
instance->y[i] = NULL;
#endif
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
for (size_t i = 0; i < DATA_PRODUCT_CONTROL_INPUTS_N; i++)
instance->c[i] = NULL;
@ -58,22 +66,39 @@ static void activate(LV2_Handle instance) {
plugin_instance * i = (plugin_instance *)instance;
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) {
i->params[j] = i->c[j] != NULL ? *i->c[j] : param_defaults[j];
i->params[j] = i->c[j] != NULL ? *i->c[j] : param_data[j].def;
plugin_set_parameter(&i->p, j, i->params[j]);
}
#endif
plugin_reset(&i->p);
}
static inline float clampf(float x, float m, float M) {
return x < m ? m : (x > M ? M : x);
}
static void run(LV2_Handle instance, uint32_t sample_count) {
plugin_instance * i = (plugin_instance *)instance;
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++)
if (*i->c[j] != i->params[j]) {
i->params[j] = *i->c[j];
plugin_set_parameter(&i->p, j, i->params[j]);
for (size_t j = 0; j < DATA_PRODUCT_CONTROL_INPUTS_N; j++) {
if (i->c[j] == NULL)
continue;
float v;
if (param_data[j].flags & DATA_PARAM_BYPASS)
v = *i->c[j] > 0.f ? 0.f : 1.f;
else if (param_data[j].flags & DATA_PARAM_TOGGLED)
v = *i->c[j] > 0.f ? 1.f : 0.f;
else
v = clampf(*i->c[j], param_data[j].min, param_data[j].max);
if (v != i->params[j]) {
i->params[j] = v;
plugin_set_parameter(&i->p, j, v);
}
}
#endif
plugin_process(&i->p, i->x, i->y, sample_count);
}

View File

@ -1,28 +0,0 @@
typedef struct plugin {
// FILL ME
char abc;
} plugin;
void plugin_init(plugin *instance) {
// WRITE ME
}
void plugin_fini(plugin *instance) {
// WRITE ME
}
void plugin_set_sample_rate(plugin *instance, float sample_rate) {
// WRITE ME
}
void plugin_reset(plugin *instance) {
// WRITE ME
}
void plugin_set_parameter(plugin *instance, size_t index, float value) {
// WRITE ME
}
void plugin_process(plugin *instance, const float **inputs, float **outputs, size_t n_samples) {
// WRITE ME
}

View File

@ -27,23 +27,11 @@ module.exports = function (data, api) {
for (var id in data.lv2.prefixes)
data.tibia.lv2.prefixes.push({ id: id, uri: data.lv2.prefixes[id] });
var symbols = {};
function getSymbol(name) {
name = name.toLowerCase().replace(/[^0-9a-z]/g, "_");
var n = name;
var i = 1;
while (n in symbols) {
n = name + i;
i++;
}
return n;
}
for (var i = 0; i < data.product.parameters.length; i++) {
var p = data.product.parameters[i];
var e = Object.create(p);
e.type = "control";
e.symbol = getSymbol(p.shortName);
e.symbol = data.lv2.parameterSymbols[i];
data.tibia.lv2.ports.push(e);
}
@ -52,7 +40,7 @@ module.exports = function (data, api) {
var b = audioBuses[i];
for (var j = 0; j < b.channels; j++) {
var e = { type: "audio", direction: b.direction, name: b.name, sidechain: b.sidechain, cv: b.cv };
e.symbol = getSymbol(b.name);
e.symbol = data.lv2.busSymbols[i] + "_" + j;
data.tibia.lv2.ports.push(e);
}
}
@ -62,5 +50,4 @@ module.exports = function (data, api) {
api.generateFileFromTemplateFile(`data${sep}manifest.ttl`, `data${sep}manifest.ttl`, data);
api.copyFile(`src${sep}lv2.c`, `src${sep}lv2.c`);
api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data);
api.copyFileIfNotExists(`src${sep}plugin.h`, `src${sep}plugin.h`);
};

View File

@ -5,8 +5,8 @@ CFLAGS = -fPIC -Wall -Wpedantic -Wextra -Wno-unused-parameter
BUNDLE_DIR = ${BUNDLE_NAME}.vst3
SO_DIR := $(shell uname -m)-linux
SO_FILE := ${SO_DIR}/${BUNDLE_NAME}.so
SO_DIR = $(shell uname -m)-linux
SO_FILE = ${SO_DIR}/${BUNDLE_NAME}.so
all: build/${BUNDLE_DIR}/Contents/Info.plist build/${BUNDLE_DIR}/Contents/${SO_FILE}
@ -14,7 +14,7 @@ build/${BUNDLE_DIR}/Contents/Info.plist: data/Info.plist | build/${BUNDLE_DIR}/C
cp $^ $@
build/${BUNDLE_DIR}/Contents/${SO_FILE}: src/vst3.c | build/${BUNDLE_DIR}/Contents/${SO_DIR}
${CC} $^ -o $@ ${CFLAGS} -I${VST3_C_API_DIR} -shared
${CC} $^ -o $@ ${CFLAGS} ${CFLAGS_EXTRA} ${LIBS_EXTRA} -shared
build/${BUNDLE_DIR}/Contents build/${BUNDLE_DIR}/Contents/${SO_DIR}:
mkdir -p $@
@ -22,4 +22,4 @@ build/${BUNDLE_DIR}/Contents build/${BUNDLE_DIR}/Contents/${SO_DIR}:
clean:
rm -fr build
.PHONY: clean
.PHONY: all clean

View File

@ -1,2 +1,3 @@
BUNDLE_NAME := {{=it.product.bundleName}}
VST3_C_API_DIR := ../../../vst3_c_api
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.vst3_make && it.vst3_make.cflags ? it.vst3_make.cflags : ""}}
LIBS_EXTRA := {{=it.make && it.make.libs ? it.make.libs : ""}} {{=it.vst3_make && it.vst3_make.libs ? it.vst3_make.libs : ""}}

View File

@ -1,28 +0,0 @@
typedef struct plugin {
// FILL ME
char abc;
} plugin;
void plugin_init(plugin *instance) {
// WRITE ME
}
void plugin_fini(plugin *instance) {
// WRITE ME
}
void plugin_set_sample_rate(plugin *instance, float sample_rate) {
// WRITE ME
}
void plugin_reset(plugin *instance) {
// WRITE ME
}
void plugin_set_parameter(plugin *instance, size_t index, float value) {
// WRITE ME
}
void plugin_process(plugin *instance, const float **inputs, float **outputs, size_t n_samples) {
// WRITE ME
}

View File

@ -5,5 +5,4 @@ module.exports = function (data, api) {
api.generateFileFromTemplateFile(`data${sep}Info.plist`, `data${sep}Info.plist`, data);
api.copyFile(`src${sep}vst3.c`, `src${sep}vst3.c`);
api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data);
api.copyFileIfNotExists(`src${sep}plugin.h`, `src${sep}plugin.h`);
};

View File

@ -5,6 +5,9 @@
},
"uri": "@example:tibia_test",
"project": "@example:project",
"types": [ "@lv2:AmplifierPlugin" ]
"types": [ "@lv2:AmplifierPlugin" ],
"version": "1.0",
"busSymbols": [ "input", "output" ],
"parameterSymbols": [ "gain", "bypass" ]
}
}

5
test/make.json Normal file
View File

@ -0,0 +1,5 @@
{
"make": {
"libs": "-lm"
}
}

View File

@ -30,6 +30,11 @@ void plugin_set_parameter(plugin *instance, size_t index, float value) {
}
}
float plugin_get_parameter(plugin *instance, size_t index) {
// no output parameters
return 0.f;
}
void plugin_process(plugin *instance, const float **inputs, float **outputs, size_t n_samples) {
const float g = instance->bypass ? 1.f : powf(10.f, 0.05f * instance->gain);
for (size_t i = 0; i < n_samples; i++)

View File

@ -10,16 +10,20 @@
"direction": "input",
"channels": 1,
"name": "Input",
"shortName": "Input",
"sidechain": false,
"cv": false
"cv": false,
"optional": false
},
{
"type": "audio",
"direction": "output",
"channels": 1,
"name": "Output",
"shortName": "Output",
"sidechain": false,
"cv": false
"cv": false,
"optional": false
}
],
"parameters": [
@ -30,9 +34,12 @@
"steps": 0,
"direction": "input",
"isBypass": false,
"isLatency": false,
"defaultValue": 0.0,
"minimum": -60.0,
"maximum": 12.0
"maximum": 12.0,
"toggled": false,
"optional": false
},
{
"name": "Bypass",
@ -41,9 +48,12 @@
"steps": 1,
"direction": "input",
"isBypass": true,
"defaultValue": 0.0,
"isLatency": false,
"defaultValue": 0,
"minimum": 0,
"maximum": 1
"maximum": 1,
"toggled": true,
"optional": true
}
]
}

View File

@ -2,8 +2,8 @@
dir=`dirname $0`
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json $dir/../templates/vst3 $dir/../out/vst3
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json $dir/../templates/vst3-make $dir/../out/vst3
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.json,$dir/make.json,$dir/vst3-make.json $dir/../templates/vst3-make $dir/../out/vst3
cp $dir/plugin.h $dir/../out/vst3/src
$dir/../tibia $dir/product.json,$dir/company.json,$dir/lv2.json $dir/../templates/lv2 $dir/../out/lv2
$dir/../tibia $dir/product.json,$dir/company.json,$dir/lv2.json $dir/../templates/lv2-make $dir/../out/lv2
$dir/../tibia $dir/product.json,$dir/company.json,$dir/lv2.json,$dir/make.json $dir/../templates/lv2-make $dir/../out/lv2
cp $dir/plugin.h $dir/../out/lv2/src

5
test/vst3-make.json Normal file
View File

@ -0,0 +1,5 @@
{
"vst3_make": {
"cflags": "-I../../../vst3_c_api"
}
}

9
tibia
View File

@ -62,15 +62,6 @@ var api = {
var dir = outputDir + path.sep + path.dirname(outFile);
fs.mkdirSync(dir, { recursive: true });
fs.copyFileSync(template + path.sep + inFile, outputDir + path.sep + outFile);
},
copyFileIfNotExists: function (inFile, outFile) {
var p = outputDir + path.sep + outFile;
if (fs.existsSync(p))
return;
var dir = outputDir + path.sep + path.dirname(outFile);
fs.mkdirSync(dir, { recursive: true });
fs.copyFileSync(template + path.sep + inFile, p);
}
};