This commit is contained in:
Stefano D'Angelo 2024-01-18 15:30:38 +01:00
parent bcc61e9a29
commit 8dabd9b3f8
5 changed files with 161 additions and 20 deletions

2
TODO
View File

@ -19,3 +19,5 @@
* LV2 (and raw midi): uneven midi pitch bend, what to do?
* VST3: noteOn, noteOff tuning, what to do? (https://steinbergmedia.github.io/vst3_doc/vstinterfaces/structSteinberg_1_1Vst_1_1NoteOnEvent.html, https://steinbergmedia.github.io/vst3_doc/vstinterfaces/structSteinberg_1_1Vst_1_1NoteOffEvent.html)
* midi out
* vst3 tail, web process return true/false
* error checking etc. web, especially processor.js

View File

@ -8,20 +8,23 @@ LDFLAGS = \
-Wl,--lto-O3 \
-Wl,-strip-all \
-Wl,--export-table \
-Wl,--export=wrapper_new \
-Wl,--export=wrapper_free \
-Wl,--export=wrapper_get_ins \
-Wl,--export=wrapper_get_outs \
-Wl,--export=wrapper_get_param_values \
-Wl,--export=wrapper_process \
-Wl,--export=wrapper_set_parameter \
-Wl,--export=processor_new \
-Wl,--export=processor_free \
-Wl,--export=processor_get_ins \
-Wl,--export=processor_get_outs \
-Wl,--export=processor_get_param_values \
-Wl,--export=processor_process \
-Wl,--export=processor_set_parameter \
-Wl,-z,stack-size=$$((8*1024*1024)) \
-nostdlib
all: build/${BUNDLE_NAME}.wasm
all: build/${BUNDLE_NAME}.wasm build/${BUNDLE_NAME}_processor.js
build/${BUNDLE_NAME}.wasm: src/data.h src/memset.h src/plugin.h src/walloc.h src/wrapper.c | build
${CC} src/wrapper.c -o $@ ${CFLAGS} ${LDFLAGS} ${CFLAGS_EXTRA} ${LIBS_EXTRA}
build/${BUNDLE_NAME}.wasm: src/data.h src/memset.h src/plugin.h src/walloc.h src/processor.c | build
${CC} src/processor.c -o $@ ${CFLAGS} ${LDFLAGS} ${CFLAGS_EXTRA} ${LIBS_EXTRA}
build/${BUNDLE_NAME}_processor.js: src/processor.js | build
cp $^ $@
build:
mkdir -p $@

View File

@ -27,7 +27,7 @@ typedef struct {
#endif
} instance;
instance * wrapper_new(float sample_rate) {
instance * processor_new(float sample_rate) {
instance * i = malloc(sizeof(instance));
if (i == NULL)
return NULL;
@ -66,32 +66,32 @@ instance * wrapper_new(float sample_rate) {
return i;
}
void wrapper_free(instance * i) {
void processor_free(instance * i) {
plugin_fini(&i->p);
if (i->mem)
free(i->mem);
free(i);
}
float * wrapper_get_x_buf(instance * i) {
float * processor_get_x_buf(instance * i) {
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
return i->x_buf;
#else
(void *)i;
(void)i;
return NULL;
#endif
}
float * wrapper_get_y_buf(instance * i) {
float * processor_get_y_buf(instance * i) {
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
return i->y_buf;
#else
(void *)i;
(void)i;
return NULL;
#endif
}
float * wrapper_get_out_params(instance *i) {
float * processor_get_out_params(instance *i) {
#if DATA_PRODUCT_PARAMETERS_OUTPUT_N > 0
return i->out_params;
#else
@ -100,11 +100,11 @@ float * wrapper_get_out_params(instance *i) {
#endif
}
void wrapper_set_parameter(instance *i, int32_t index, float value) {
void processor_set_parameter(instance *i, int32_t index, float value) {
plugin_set_parameter(&i->p, index, value);
}
void wrapper_process(instance *i, int32_t n_samples) {
void processor_process(instance *i, int32_t n_samples) {
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
const float **x = i->x;
#else

View File

@ -0,0 +1,135 @@
/*
* Copyright (C) 2022, 2024 Orastron Srl unipersonale
*/
var buses = {{=JSON.stringify(it.product.buses, null, 2)}};
var parameters = {{=JSON.stringify(it.product.parameters, null, 2)}};
class Processor extends AudioWorkletProcessor {
throwError(msg) {
this.port.postMessage({ type: "error", msg: msg);
throw msg;
}
constructor(options) {
super();
var module = new WebAssembly.Module(options.processorOptions.wasmBytes);
var instance = new WebAssembly.Instance(module, { env: {} });
this.module = instance.exports;
this.instance = this.module.processor_new(sampleRate);
if (!this.instance)
this.throwError("Could not instantiate processor module");
function getBuffers(p, output) {
var ret = [];
for (var i = 0; i < buses.length; i++) {
if ((output && buses[i].direction == "input") || (!output && buses[i].direction == "output"))
continue;
if (buses[i].channels == "mono") {
ret.push([ new Float32Array(this.module.memory.buffer, p, 128) ]);
p += 128 * 4;
} else {
ret.push([
new Float32Array(this.module.memory.buffer, p, 128),
new Float32Array(this.module.memory.buffer, p + 128 * 4, 128)
]);
p += 2 * 128 * 4;
}
}
return ret;
}
this.x = getBuffers.call(this, this.module.processor_get_x_buf(this.instance), false);
this.y = getBuffers.call(this, this.module.processor_get_y_buf(this.instance), true);
this.parametersIn = [];
for (var i = 0; i < parameters.length; i++)
if (parameters[i].direction == "input")
this.parametersIn.push({ index: i, value: NaN });
this.parametersOut = [];
for (var i = 0; i < parameters.length; i++)
if (parameters[i].direction == "output")
this.parametersOut.push({ index: i, value: NaN });
if (this.parametersOut.length > 0)
this.parametersOutValues = new Float32Array(this.module.memory.buffer, this.module.processor_get_out_params(this.instance), this.parametersOut.length);
this.paramOutChangeMsg = { type: "paramOutChange", index: NaN, value: NaN };
}
process(input, outputs, params) {
for (var i = 0; i < this.parametersIn.length; i++) {
var index = this.parametersIn[i].index;
var parameter = parameters[index];
var name = parameter.name;
var value = params[p.name][0];
if (value != this.parametersIn[i].value) {
if (parameter.isBypass || parameter.toggled)
value = value > 0 ? 0 : 1;
else if (parameter.integer)
value = Math.round(value);
value = Math.min(Math.max(value, parameter.minimum), parameter.maximum);
if (value != this.parametersIn[i].value) {
this.module.processor_set_parameter(this.instance, index, value);
this.parametersIn[i].value = value;
}
}
}
var n = outputs[0][0].length;
var i = 0;
while (i < n) {
var s = Math.min(n - i, 128);
for (var j = 0; j < this.x.length; j++) {
var input = inputs[j];
if (!input.length) {
for (var k = 0; k < this.x[j].length; k++)
this.x[j][k].fill(0);
} else {
for (var k = 0; k < this.x[j].length; k++)
if (k <= input.length)
this.x[j][k].set(input[k].subarray(i, s));
else
this.x[j][k].fill(0);
}
}
this.module.processor_process(this.instance, s);
for (var j = 0; j < this.y.length; j++) {
var output = outputs[j];
for (var k = 0; k < this.y[j].length; k++)
this.y[j][k].set(output[k].subarray(i, s));
}
i += s;
}
for (var i = 0; i < this.parametersOut.length; i++) {
var index = this.parametersOut[i].index;
var value = this.parametersOutValues[i];
if (value != this.parametersOut[i].value) {
this.paramOutChangeMsg.index = index;
this.paramOutChangeMsg.value = value;
this.port.postMessage(this.paramOutChangeMsg);
this.parametersOut[i].value = value;
}
}
return false;
}
static get parameterDescriptors() {
var ret = [];
for (var i = 0; i < this.parametersIn.length; i++) {
var p = parameters[this.parametersIn[i].index];
ret.push({ name: p.name, minValue: p.minimum, maxValue: p.maximum, defaultValue: p.defaultValue, automationRate: "k-rate" });
}
return ret;
}
}
registerProcessor("{{=it.product.bundleName}}", Processor);

View File

@ -4,6 +4,7 @@ var sep = path.sep;
module.exports = function (data, api) {
api.copyFile(`src${sep}memset.h`, `src${sep}memset.h`);
api.copyFile(`src${sep}walloc.h`, `src${sep}walloc.h`);
api.copyFile(`src${sep}wrapper.c`, `src${sep}wrapper.c`);
api.copyFile(`src${sep}processor.c`, `src${sep}processor.c`);
api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data);
api.generateFileFromTemplateFile(`src${sep}processor.js`, `src${sep}processor.js`, data);
};