beginning of web + fixes/adjustments

This commit is contained in:
Stefano D'Angelo 2024-01-17 17:31:14 +01:00
parent 3b6a621d8e
commit bcc61e9a29
13 changed files with 374 additions and 18 deletions

View File

@ -254,7 +254,7 @@ static void run(LV2_Handle instance, uint32_t sample_count) {
for (uint32_t j = 0; j < DATA_PRODUCT_CONTROL_OUTPUTS_N; j++) {
uint32_t k = param_out_index[j];
if (i->c[k] != NULL)
i->c[k] = plugin_get_parameter(&i->p, k);
*i->c[k] = plugin_get_parameter(&i->p, k);
}
#endif
}

View File

@ -0,0 +1,32 @@
include vars.mk
CC = clang
CFLAGS = --target=wasm32 -flto -fvisibility=hidden -Ofast -Wall -Wextra -Wpedantic
LDFLAGS = \
-Wl,--allow-undefined \
-Wl,--no-entry \
-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,-z,stack-size=$$((8*1024*1024)) \
-nostdlib
all: build/${BUNDLE_NAME}.wasm
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:
mkdir -p $@
clean:
rm -fr build
.PHONY: all clean

View File

@ -0,0 +1,4 @@
module.exports = function (data, api) {
api.copyFile(`Makefile`, `Makefile`);
api.generateFileFromTemplateFile(`vars.mk`, `vars.mk`, data);
};

View File

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

26
templates/web/src/data.h Normal file
View File

@ -0,0 +1,26 @@
#define DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").reduce((s, x) => s += x.channels == "mono" ? 1 : 2, 0)}}
#define DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").reduce((s, x) => s += x.channels == "mono" ? 1 : 2, 0)}}
#define DATA_PRODUCT_MIDI_INPUTS_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "input").length}}
#define DATA_PRODUCT_MIDI_OUTPUTS_N {{=it.product.buses.filter(x => x.type == "midi" && x.direction == "output").length}}
#define DATA_PRODUCT_PARAMETERS_N {{=it.product.parameters.length}}
#define DATA_PRODUCT_PARAMETERS_OUTPUT_N {{=it.product.parameters.filter(x => x.direction == "output").length}}
#if DATA_PRODUCT_PARAMETERS_N > 0
static struct {
char out;
float def;
} param_data[DATA_PRODUCT_PARAMETERS_N] = {
{{~it.product.parameters :p}}
{
/* .out = */ {{=p.direction == "output" ? 1 : 0}},
/* .def = */ {{=p.defaultValue.toExponential()}}
},
{{~}}
};
#endif
#if DATA_PRODUCT_PARAMETERS_OUTPUT_N > 0
static size_t param_out_index[DATA_PRODUCT_PARAMETERS_OUTPUT_N] = {
{{~it.product.parameters :p:i}}{{?p.direction == "output"}}{{=i}}, {{?}}{{~}}
};
#endif

View File

@ -0,0 +1,10 @@
/*
* Copyright (C) 2023 Orastron Srl unipersonale
*/
void *memset(void *ptr, int value, size_t num) {
unsigned char *p = (unsigned char *)ptr;
for (size_t i = 0; i < num; i++)
p[i] = (unsigned char)value;
return ptr;
}

127
templates/web/src/walloc.h Normal file
View File

@ -0,0 +1,127 @@
/*
* Copyright (C) 2021, 2022, 2024 Orastron Srl unipersonale
*/
void *malloc(size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
extern unsigned char __heap_base;
typedef struct _header {
struct _header *next;
struct _header *prev;
char free;
} header;
static char inited = 0;
static size_t get_size(header *h) {
char *n = (char *)h->next;
return (n ? n : (char *)(__builtin_wasm_memory_size(0) << 16)) - (char *)h - sizeof(header);
}
static void split_if_possible(header *h, size_t s, size_t size) {
if (s <= size + sizeof(header) + sizeof(header))
return;
header *hn = (header *)((char *)h + sizeof(header) + size);
hn->prev = h;
hn->next = h->next;
hn->free = 1;
h->next = hn;
if (hn->next)
hn->next->prev = hn;
}
void *malloc(size_t size) {
if (size == 0)
return NULL;
header *h = (header *)&__heap_base;
if (!inited) {
h->next = NULL;
h->prev = NULL;
h->free = 1;
inited = 1;
}
header *p;
for (; h; p = h, h = h->next) {
if (!h->free)
continue;
size_t s = get_size(h);
if (s < size)
continue;
split_if_possible(h, s, size);
h->free = 0;
return (char *)h + sizeof(header);
}
int32_t n = __builtin_wasm_memory_grow(0, ((size + sizeof(header) - 1) >> 16) + 1);
if (n < 0)
return NULL;
if (p->free)
h = p;
else {
h = (header *)(n << 16);
p->next = h;
h->prev = p;
h->next = NULL;
}
split_if_possible(h, get_size(h), size);
h->free = 0;
return (char *)h + sizeof(header);
}
void *realloc(void *ptr, size_t size) {
if (ptr == NULL)
return malloc(size);
if (size == 0) {
free(ptr);
return NULL;
}
header *h = (header *)((char *)ptr - sizeof(header));
size_t s = get_size(h);
if (s >= size)
return ptr;
void *p = malloc(size);
if (p == NULL)
return NULL;
char *src = (char *)ptr;
char *dest = (char *)p;
for (size_t i = 0; i < s; i++)
dest[i] = src[i];
free(ptr);
return p;
}
void free(void *ptr) {
header *h = (header *)((char *)ptr - sizeof(header));
h->free = 1;
if (h->next && h->next->free) {
h->next = h->next->next;
if (h->next)
h->next->prev = h;
}
if (h->prev && h->prev->free) {
h->prev->next = h->next;
if (h->next)
h->next->prev = h->prev;
}
}

125
templates/web/src/wrapper.c Normal file
View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2022-2024 Orastron Srl unipersonale
*/
#include <stddef.h>
#include <stdint.h>
#include "data.h"
#include "plugin.h"
#include "memset.h"
#include "walloc.h"
typedef struct {
plugin p;
void * mem;
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
float x_buf[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N * 128];
const float * x[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N];
#endif
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
float y_buf[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N * 128];
float * y[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N];
#endif
#if DATA_PRODUCT_PARAMETERS_OUTPUT_N > 0
float out_params[DATA_PRODUCT_PARAMETERS_OUTPUT_N];
#endif
} instance;
instance * wrapper_new(float sample_rate) {
instance * i = malloc(sizeof(instance));
if (i == NULL)
return NULL;
plugin_init(&i->p);
#if DATA_PRODUCT_PARAMETERS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_PARAMETERS_N; j++)
if (!param_data[j].out)
plugin_set_parameter(&i->p, j, param_data[j].def);
#endif
plugin_set_sample_rate(&i->p, sample_rate);
size_t req = plugin_mem_req(&i->p);
if (req != 0) {
i->mem = malloc(req);
if (i->mem == NULL) {
plugin_fini(&i->p);
return NULL;
}
plugin_mem_set(&i->p, i->mem);
} else
i->mem = NULL;
plugin_reset(&i->p);
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N; j++)
i->x[j] = i->x_buf + 128 * j;
#endif
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
for (size_t j = 0; j < DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N; j++)
i->y[j] = i->y_buf + 128 * j;
#endif
return i;
}
void wrapper_free(instance * i) {
plugin_fini(&i->p);
if (i->mem)
free(i->mem);
free(i);
}
float * wrapper_get_x_buf(instance * i) {
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
return i->x_buf;
#else
(void *)i;
return NULL;
#endif
}
float * wrapper_get_y_buf(instance * i) {
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
return i->y_buf;
#else
(void *)i;
return NULL;
#endif
}
float * wrapper_get_out_params(instance *i) {
#if DATA_PRODUCT_PARAMETERS_OUTPUT_N > 0
return i->out_params;
#else
(void)i;
return NULL;
#endif
}
void wrapper_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) {
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
const float **x = i->x;
#else
const float **x = NULL;
#endif
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
float **y = i->y;
#else
float **y = NULL;
#endif
plugin_process(&i->p, x, y, n_samples);
#if DATA_PRODUCT_PARAMETERS_OUTPUT_N > 0
for (size_t j = 0; j < DATA_PRODUCT_PARAMETERS_OUTPUT_N; j++)
i->out_params[j] = plugin_get_parameter(&i->p, param_out_index[j]);
#endif
}

View File

@ -0,0 +1,9 @@
var path = require("path");
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.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data);
};

View File

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

View File

@ -1,6 +1,3 @@
#include <string.h>
#include <math.h>
typedef struct plugin {
float sample_rate;
size_t delay_line_length;
@ -14,6 +11,7 @@ typedef struct plugin {
size_t delay_line_cur;
float z1;
float cutoff_k;
float yz1;
} plugin;
static void plugin_init(plugin *instance) {
@ -24,7 +22,8 @@ static void plugin_fini(plugin *instance) {
static void plugin_set_sample_rate(plugin *instance, float sample_rate) {
instance->sample_rate = sample_rate;
instance->delay_line_length = ceilf(sample_rate) + 1;
//safe approx instance->delay_line_length = ceilf(sample_rate) + 1;
instance->delay_line_length = (size_t)(sample_rate + 1.f) + 1;
}
static size_t plugin_mem_req(plugin *instance) {
@ -36,16 +35,19 @@ static void plugin_mem_set(plugin *instance, void *mem) {
}
static void plugin_reset(plugin *instance) {
memset(instance->delay_line, 0, instance->delay_line_length * sizeof(float));
for (size_t i = 0; i < instance->delay_line_length; i++)
instance->delay_line[i] = 0.f;
instance->delay_line_cur = 0;
instance->z1 = 0.f;
instance->cutoff_k = 1.f;
instance->yz1 = 0.f;
}
static void plugin_set_parameter(plugin *instance, size_t index, float value) {
switch (index) {
case 0:
instance->gain = powf(10.f, 0.05f * value);
//approx instance->gain = powf(10.f, 0.05f * value);
instance->gain = ((2.6039890429412597e-4f * value + 0.032131027163547855f) * value + 1.f) / ((0.0012705124328080768f * value - 0.0666763481312185f) * value + 1.f);
break;
case 1:
instance->delay = 0.001f * value;
@ -60,8 +62,7 @@ static void plugin_set_parameter(plugin *instance, size_t index, float value) {
}
static float plugin_get_parameter(plugin *instance, size_t index) {
// no output parameters
return 0.f;
return instance->yz1;
}
static size_t calc_index(size_t cur, size_t delay, size_t len) {
@ -69,7 +70,8 @@ static size_t calc_index(size_t cur, size_t delay, size_t len) {
}
static void plugin_process(plugin *instance, const float **inputs, float **outputs, size_t n_samples) {
size_t delay = roundf(instance->sample_rate * instance->delay);
//approx size_t delay = roundf(instance->sample_rate * instance->delay);
size_t delay = (size_t)(instance->sample_rate * instance->delay + 0.5f);
const float mA1 = instance->sample_rate / (instance->sample_rate + 6.283185307179586f * instance->cutoff * instance->cutoff_k);
for (size_t i = 0; i < n_samples; i++) {
instance->delay_line[instance->delay_line_cur] = inputs[0][i];
@ -80,11 +82,13 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu
const float y = x + mA1 * (instance->z1 - x);
instance->z1 = y;
outputs[0][i] = instance->bypass ? inputs[0][i] : instance->gain * y;
instance->yz1 = outputs[0][i];
}
}
static void plugin_note_on(plugin *instance, size_t index, uint8_t note, float velocity) {
instance->cutoff_k = powf(2.f, (1.f / 12.f) * (note - 60));
//approx instance->cutoff_k = powf(2.f, (1.f / 12.f) * (note - 60));
instance->cutoff_k = note < 64 ? (-0.19558034980097166f * note - 2.361735109225749f) / (note - 75.57552349522389f) : (393.95397927344214f - 7.660826245588588f * note) / (note - 139.0755234952239f);
}
static void plugin_note_off(plugin *instance, size_t index, uint8_t note, float velocity) {

View File

@ -108,6 +108,22 @@
"list": true,
"unit": "",
"map": "linear"
},
{
"name": "yz1",
"shortName": "yz1",
"direction": "output",
"isBypass": false,
"isLatency": false,
"defaultValue": 0.0,
"minimum": -1.0,
"maximum": 1.0,
"toggled": false,
"optional": false,
"integer": false,
"list": false,
"unit": "",
"map": "linear"
}
]
}

View File

@ -2,8 +2,13 @@
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/make.json,$dir/vst3-make.json $dir/../templates/vst3-make $dir/../out/vst3
$dir/../tibia $dir/product.json,$dir/company.json,$dir/vst3.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/make.json $dir/../templates/lv2-make $dir/../out/lv2
$dir/../tibia $dir/product.json,$dir/company.json,$dir/lv2.json $dir/../templates/lv2-make $dir/../out/lv2
cp $dir/plugin.h $dir/../out/lv2/src
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/web $dir/../out/web
$dir/../tibia $dir/product.json,$dir/company.json $dir/../templates/web-make $dir/../out/web
cp $dir/plugin.h $dir/../out/web/src