beginning of web + fixes/adjustments
This commit is contained in:
parent
3b6a621d8e
commit
bcc61e9a29
@ -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
|
||||
}
|
||||
|
32
templates/web-make/Makefile
Normal file
32
templates/web-make/Makefile
Normal 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
|
4
templates/web-make/tibia-index.js
Normal file
4
templates/web-make/tibia-index.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = function (data, api) {
|
||||
api.copyFile(`Makefile`, `Makefile`);
|
||||
api.generateFileFromTemplateFile(`vars.mk`, `vars.mk`, data);
|
||||
};
|
3
templates/web-make/vars.mk
Normal file
3
templates/web-make/vars.mk
Normal 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
26
templates/web/src/data.h
Normal 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
|
10
templates/web/src/memset.h
Normal file
10
templates/web/src/memset.h
Normal 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
127
templates/web/src/walloc.h
Normal 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
125
templates/web/src/wrapper.c
Normal 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
|
||||
}
|
9
templates/web/tibia-index.js
Normal file
9
templates/web/tibia-index.js
Normal 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);
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"make": {
|
||||
"libs": "-lm"
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user