cmd initial code
This commit is contained in:
parent
564aae4e2d
commit
9026609bea
4
notes
4
notes
@ -144,9 +144,9 @@ product {
|
||||
web: not used
|
||||
list:
|
||||
parameter is a list (using scalePoints values)? default false
|
||||
VST3: TBD
|
||||
VST3: TBD (+approx to closest?)
|
||||
LV2: manifest.ttl lv2:port lv2:enumeration - run() (set parameter) TBD?
|
||||
web: not used
|
||||
web: TBD (approx to closest? dropdown? both?)
|
||||
unit:
|
||||
unit of measure (from predefined list, see tibia-index.js), default ""
|
||||
VST3: ParameterInfo units
|
||||
|
18
templates/cmd-make/Makefile
Normal file
18
templates/cmd-make/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
include vars.mk
|
||||
|
||||
CC = gcc
|
||||
|
||||
CFLAGS = -I${TINYWAV_DIR} -fPIC -Wall -Wpedantic -Wextra -Wno-unused-parameter
|
||||
|
||||
all: build/${BUNDLE_NAME}
|
||||
|
||||
build/${BUNDLE_NAME}: src/main.c ${TINYWAV_DIR}/tinywav.c | build
|
||||
${CC} $^ -o $@ ${CFLAGS} ${CFLAGS_EXTRA} ${LDFLAGS_EXTRA}
|
||||
|
||||
build:
|
||||
mkdir -p $@
|
||||
|
||||
clean:
|
||||
rm -fr build
|
||||
|
||||
.PHONY: all clean
|
4
templates/cmd-make/tibia-index.js
Normal file
4
templates/cmd-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);
|
||||
};
|
4
templates/cmd-make/vars.mk
Normal file
4
templates/cmd-make/vars.mk
Normal file
@ -0,0 +1,4 @@
|
||||
BUNDLE_NAME := {{=it.product.bundleName}}
|
||||
CFLAGS_EXTRA := {{=it.make && it.make.cflags ? it.make.cflags : ""}} {{=it.cmd_make.cflags ? it.cmd_make.cflags : ""}}
|
||||
LDFLAGS_EXTRA := {{=it.make && it.make.ldflags ? it.make.ldflags : ""}} {{=it.cmd_make.ldflags ? it.cmd_make.ldflags : ""}}
|
||||
TINYWAV_DIR := {{=it.cmd_make.tinywavDir}}
|
34
templates/cmd/src/data.h
Normal file
34
templates/cmd/src/data.h
Normal file
@ -0,0 +1,34 @@
|
||||
#define AUDIO_BUS_IN {{=it.product.buses.findIndex(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain)}}
|
||||
#define AUDIO_BUS_OUT {{=it.product.buses.findIndex(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain)}}
|
||||
|
||||
#define NUM_CHANNELS_IN {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain) ? (it.product.buses.filter(x => x.type == "audio" && x.direction == "input" && !x.cv && !x.sidechain)[0].channels == "mono" ? 1 : 2) : 0}}
|
||||
#define NUM_CHANNELS_OUT {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain) ? (it.product.buses.filter(x => x.type == "audio" && x.direction == "output" && !x.cv && !x.sidechain)[0].channels == "mono" ? 1 : 2) : 0}}
|
||||
|
||||
#define PARAMETERS_N {{=it.product.parameters.length}}
|
||||
|
||||
#if PARAMETERS_N > 0
|
||||
|
||||
# define PARAM_BYPASS 1
|
||||
# define PARAM_TOGGLED (1<<1)
|
||||
# define PARAM_INTEGER (1<<2)
|
||||
|
||||
static struct {
|
||||
const char * id;
|
||||
char out;
|
||||
float def;
|
||||
float min;
|
||||
float max;
|
||||
uint32_t flags;
|
||||
} param_data[PARAMETERS_N] = {
|
||||
{{~it.product.parameters :p:i}}
|
||||
{
|
||||
/* .id = */ "{{=it.cmd.parameterIds[i]}}",
|
||||
/* .out = */ {{=p.direction == "output" ? 1 : 0}},
|
||||
/* .def = */ {{=p.defaultValue.toExponential()}}f,
|
||||
/* .min = */ {{=p.minimum.toExponential()}}f,
|
||||
/* .max = */ {{=p.maximum.toExponential()}}f,
|
||||
/* .flags = */ {{?p.isBypass}}PARAM_BYPASS{{??p.isLatency}}PARAM_INTEGER{{??}}0{{?p.toggled}} | PARAM_TOGGLED{{?}}{{?p.integer}} | PARAM_INTEGER{{?}}{{?}}
|
||||
},
|
||||
{{~}}
|
||||
};
|
||||
#endif
|
373
templates/cmd/src/main.c
Normal file
373
templates/cmd/src/main.c
Normal file
@ -0,0 +1,373 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "data.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <tinywav.h>
|
||||
|
||||
plugin instance;
|
||||
void * mem;
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
float * x[NUM_CHANNELS_IN];
|
||||
#endif
|
||||
#if NUM_CHANNELS_OUT > 0
|
||||
float * y[NUM_CHANNELS_OUT];
|
||||
#endif
|
||||
float fs = 44100.f;
|
||||
size_t bufsize = 128;
|
||||
#if NUM_CHANNELS_IN == 0
|
||||
float length = 1.f;
|
||||
#endif
|
||||
#if PARAMETERS_N > 0
|
||||
float param_values[PARAMETERS_N];
|
||||
#endif
|
||||
const char * infile = NULL;
|
||||
const char * outfile = NULL;
|
||||
|
||||
void usage(const char * argv0) {
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
fprintf(stderr, "Usage: %s [bufsize=value] infile", argv0);
|
||||
#else
|
||||
fprintf(stderr, "Usage: %s [fs=value] [bufsize=value] [length=value]", argv0);
|
||||
#endif
|
||||
#if NUM_CHANNELS_OUT > 0
|
||||
fprintf(stderr, " outfile");
|
||||
#endif
|
||||
#if PARAMETERS_N > 0
|
||||
fprintf(stderr, " [param=value] ...");
|
||||
#endif
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
fprintf(stderr, " defaults: bufsize=128");
|
||||
#else
|
||||
fprintf(stderr, " defaults: fs=44100, bufsize=128");
|
||||
#endif
|
||||
#if PARAMETERS_N > 0
|
||||
for (size_t i = 0; i < PARAMETERS_N; i++)
|
||||
if (!param_data[i].out)
|
||||
fprintf(stderr, ", %s=%g", param_data[i].id, param_data[i].def);
|
||||
#endif
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
float clampf(float x, float m, float M) {
|
||||
return x < m ? m : (x > M ? M : x);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
#if PARAMETERS_N > 0
|
||||
for (size_t i = 0; i < PARAMETERS_N; i++)
|
||||
param_values[i] = param_data[i].def;
|
||||
#endif
|
||||
|
||||
char parsingState = 0; // 0 = fs/bufsize/length, 1 = filenames, 2 = params
|
||||
for (int i = 1; i < argc; i++) {
|
||||
switch (parsingState) {
|
||||
case 0:
|
||||
{
|
||||
char * c = strchr(argv[i], '=');
|
||||
if (c == NULL) {
|
||||
parsingState = 1;
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
if (strncmp(argv[i], "bufsize", 7) == 0) {
|
||||
char * e;
|
||||
ssize_t v = strtol(c + 1, &e, 10);
|
||||
if (errno || v <= 0 || *e != '\0') {
|
||||
fprintf(stderr, "invalid format of argument '%s'\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
bufsize = v;
|
||||
#if NUM_CHANNELS_IN == 0
|
||||
} else if (strncmp(argv[i], "fs", 2) == 0) {
|
||||
char * e;
|
||||
float v = strtof(c + 1, &e);
|
||||
if (errno || !isfinite(v) || v <= 0.f || *e != '\0') {
|
||||
fprintf(stderr, "invalid format of argument '%s'\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
fs = v;
|
||||
} else if (strncmp(argv[i], "length", 6) == 0) {
|
||||
char * e;
|
||||
float v = strtof(c + 1, &e);
|
||||
if (errno || !isfinite(v) || v <= 0.f || *e != '\0') {
|
||||
fprintf(stderr, "invalid format of argument '%s'\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
length = v;
|
||||
#endif
|
||||
} else {
|
||||
fprintf(stderr, "invalid format of argument '%s'\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
char * c = strchr(argv[i], '=');
|
||||
if (c != NULL) {
|
||||
#if PARAMETERS_N == 0
|
||||
fprintf(stderr, "invalid format of argument '%s'\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
#endif
|
||||
parsingState = 2;
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
const char ** next = NULL;
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
if (infile == NULL)
|
||||
next = &infile;
|
||||
#endif
|
||||
#if NUM_CHANNELS_OUT > 0
|
||||
if (next == NULL && outfile == NULL)
|
||||
next = &outfile;
|
||||
#endif
|
||||
if (next == NULL) {
|
||||
fprintf(stderr, "invalid argument '%s' (in/out files already specified)\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
*next = argv[i];
|
||||
}
|
||||
break;
|
||||
#if PARAMETERS_N > 0
|
||||
case 2:
|
||||
{
|
||||
char * c = strchr(argv[i], '=');
|
||||
if (c == NULL) {
|
||||
fprintf(stderr, "invalid format of argument '%s'\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
char * e;
|
||||
float v = strtof(c + 1, &e);
|
||||
if (errno || !isfinite(v) || *e != '\0') {
|
||||
fprintf(stderr, "invalid format of argument '%s'\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
int len = c - argv[i];
|
||||
int j = 0;
|
||||
for (; j < PARAMETERS_N; j++) {
|
||||
if (strncmp(argv[i], param_data[j].id, len) == 0 && param_data[j].id[len] == '\0')
|
||||
break;
|
||||
}
|
||||
if (j == PARAMETERS_N) {
|
||||
fprintf(stderr, "parameter for '%s' not found\n", argv[i]);
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
param_values[j] = v;
|
||||
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
if (infile == NULL) {
|
||||
fprintf(stderr, "infile not specified\n");
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
#if NUM_CHANNELS_OUT > 0
|
||||
if (outfile == NULL) {
|
||||
fprintf(stderr, "outfile not specified\n");
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if PARAMETERS_N > 0
|
||||
for (size_t i = 0; i < PARAMETERS_N; i++) {
|
||||
if (param_data[i].out)
|
||||
continue;
|
||||
float v = param_values[i];
|
||||
if (param_data[i].flags & (PARAM_BYPASS | PARAM_TOGGLED))
|
||||
v = v > 0.5f ? 1.f : 0.f;
|
||||
else if (param_data[i].flags & PARAM_INTEGER)
|
||||
v = (int32_t)(v + 0.5f);
|
||||
|
||||
param_values[i] = clampf(v, param_data[i].min, param_data[i].max);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
TinyWav tw_in;
|
||||
if (tinywav_open_read(&tw_in, infile, TW_SPLIT) != 0)
|
||||
return EXIT_FAILURE;
|
||||
if (tw_in.h.NumChannels != NUM_CHANNELS_IN) {
|
||||
fprintf(stderr, "input file has %d channels but %d channels were expected\n", tw_in.h.NumChannels, NUM_CHANNELS_IN);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
fs = tw_in.h.SampleRate;
|
||||
#endif
|
||||
|
||||
printf(" fs: %g\n", fs);
|
||||
printf(" bufsize: %zu\n", bufsize);
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
printf(" length: %g\n", (double)tw_in.numFramesInHeader / (double)tw_in.h.SampleRate);
|
||||
printf(" infile: %s\n", infile);
|
||||
#else
|
||||
printf(" length: %g\n", length);
|
||||
#endif
|
||||
#if NUM_CHANNELS_OUT > 0
|
||||
printf(" outfile: %s\n", outfile);
|
||||
#endif
|
||||
#if PARAMETERS_N > 0
|
||||
for (size_t i = 0; i < PARAMETERS_N; i++)
|
||||
if (!param_data[i].out)
|
||||
printf(" %s: %g\n", param_data[i].id, param_values[i]);
|
||||
#endif
|
||||
|
||||
plugin_init(&instance);
|
||||
|
||||
#if PARAMETERS_N > 0
|
||||
for (size_t i = 0; i < PARAMETERS_N; i++)
|
||||
if (!param_data[i].out)
|
||||
plugin_set_parameter(&instance, i, param_values[i]);
|
||||
#endif
|
||||
|
||||
plugin_set_sample_rate(&instance, fs);
|
||||
size_t req = plugin_mem_req(&instance);
|
||||
if (req != 0) {
|
||||
mem = malloc(req);
|
||||
if (mem == NULL) {
|
||||
plugin_fini(&instance);
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
tinywav_close_read(&tw_in);
|
||||
#endif
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
plugin_mem_set(&instance, mem);
|
||||
} else
|
||||
mem = NULL;
|
||||
|
||||
plugin_reset(&instance);
|
||||
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
float * x_buf = malloc(NUM_CHANNELS_IN * bufsize * sizeof(float));
|
||||
if (x_buf == NULL) {
|
||||
if (mem != NULL)
|
||||
free(mem);
|
||||
plugin_fini(&instance);
|
||||
tinywav_close_read(&tw_in);
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
for (size_t i = 0; i < NUM_CHANNELS_IN; i++)
|
||||
x[i] = x_buf + i * bufsize;
|
||||
#else
|
||||
float ** x = NULL;
|
||||
#endif
|
||||
|
||||
#if NUM_CHANNELS_OUT > 0
|
||||
float * y_buf = malloc(NUM_CHANNELS_OUT * bufsize * sizeof(float));
|
||||
if (y_buf == NULL) {
|
||||
# if NUM_CHANNELS_IN > 0
|
||||
free(x_buf);
|
||||
# endif
|
||||
if (mem != NULL)
|
||||
free(mem);
|
||||
plugin_fini(&instance);
|
||||
# if NUM_CHANNELS_IN > 0
|
||||
tinywav_close_read(&tw_in);
|
||||
# endif
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
for (size_t i = 0; i < NUM_CHANNELS_IN; i++)
|
||||
y[i] = y_buf + i * bufsize;
|
||||
#else
|
||||
float ** y = NULL;
|
||||
#endif
|
||||
|
||||
#if NUM_CHANNELS_OUT > 0
|
||||
TinyWav tw_out;
|
||||
if (tinywav_open_write(&tw_out, 1, fs, TW_FLOAT32, TW_SPLIT, outfile) != 0) {
|
||||
free(y_buf);
|
||||
# if NUM_CHANNELS_IN > 0
|
||||
free(x_buf);
|
||||
# endif
|
||||
if (mem != NULL)
|
||||
free(mem);
|
||||
plugin_fini(&instance);
|
||||
# if NUM_CHANNELS_IN > 0
|
||||
tinywav_close_read(&tw_in);
|
||||
# endif
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
while (1) {
|
||||
int32_t n = tinywav_read_f(&tw_in, x, bufsize);
|
||||
if (n == 0)
|
||||
break;
|
||||
plugin_process(&instance, (const float **)x, y, n);
|
||||
# if PARAMETERS_N > 0
|
||||
for (size_t j = 0; j < PARAMETERS_N; j++) {
|
||||
if (!param_data[j].out)
|
||||
continue;
|
||||
param_values[j] = plugin_get_parameter(&instance, j);
|
||||
printf(" %s: %g\n", param_data[j].id, param_values[j]);
|
||||
}
|
||||
# endif
|
||||
# if NUM_CHANNELS_OUT > 0
|
||||
tinywav_write_f(&tw_out, y, n);
|
||||
# endif
|
||||
}
|
||||
#else
|
||||
size_t i = 0;
|
||||
size_t len = (size_t)(tw_in.h.SampleRate * length + 0.5f);
|
||||
while (i < len) {
|
||||
size_t left = len - i;
|
||||
size_t n = left > bufsize ? bufsize : left;
|
||||
plugin_process(&instance, (const float **)x, y, n);
|
||||
# if PARAMETERS_N > 0
|
||||
for (size_t j = 0; j < PARAMETERS_N; j++) {
|
||||
if (!param_data[j].out)
|
||||
continue;
|
||||
param_values[j] = plugin_get_parameter(&instance, j);
|
||||
printf(" %s: %g\n", param_data[j].id, param_values[j]);
|
||||
}
|
||||
# endif
|
||||
# if NUM_CHANNELS_OUT > 0
|
||||
tinywav_write_f(&tw_out, y, n);
|
||||
# endif
|
||||
i += n;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NUM_CHANNELS_OUT > 0
|
||||
tinywav_close_write(&tw_out);
|
||||
free(y_buf);
|
||||
#endif
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
free(x_buf);
|
||||
#endif
|
||||
if (mem != NULL)
|
||||
free(mem);
|
||||
plugin_fini(&instance);
|
||||
#if NUM_CHANNELS_IN > 0
|
||||
tinywav_close_read(&tw_in);
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
7
templates/cmd/tibia-index.js
Normal file
7
templates/cmd/tibia-index.js
Normal file
@ -0,0 +1,7 @@
|
||||
var path = require("path");
|
||||
var sep = path.sep;
|
||||
|
||||
module.exports = function (data, api) {
|
||||
api.generateFileFromTemplateFile(`src${sep}data.h`, `src${sep}data.h`, data);
|
||||
api.copyFile(`src${sep}main.c`, `src${sep}main.c`);
|
||||
};
|
5
test/cmd-make.json
Normal file
5
test/cmd-make.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"cmd_make": {
|
||||
"tinywavDir": "../../../tinywav"
|
||||
}
|
||||
}
|
6
test/cmd.json
Normal file
6
test/cmd.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"cmd": {
|
||||
"busIds": [ "input", "output", "midi_in", "midi_out" ],
|
||||
"parameterIds": [ "gain", "delay", "cutoff", "bypass", "yz1" ]
|
||||
}
|
||||
}
|
@ -8,6 +8,6 @@
|
||||
"types": [ "@lv2:AmplifierPlugin" ],
|
||||
"version": "1.0",
|
||||
"busSymbols": [ "input", "output", "midi_in", "midi_out" ],
|
||||
"parameterSymbols": [ "gain", "delay", "cutoff", "enabled" ]
|
||||
"parameterSymbols": [ "gain", "delay", "cutoff", "enabled", "yz1" ]
|
||||
}
|
||||
}
|
||||
|
@ -18,3 +18,7 @@ $dir/../tibia $dir/product.json,$dir/company.json,$dir/android.json $dir/../temp
|
||||
$dir/../tibia $dir/product.json,$dir/company.json,$dir/android.json,$dir/android-make.json $dir/../templates/android-make $dir/../out/android
|
||||
cp $dir/keystore.jks $dir/../out/android
|
||||
cp $dir/plugin.h $dir/../out/android/src
|
||||
|
||||
$dir/../tibia $dir/product.json,$dir/company.json,$dir/cmd.json $dir/../templates/cmd $dir/../out/cmd
|
||||
$dir/../tibia $dir/product.json,$dir/company.json,$dir/cmd.json,$dir/cmd-make.json $dir/../templates/cmd-make $dir/../out/cmd
|
||||
cp $dir/plugin.h $dir/../out/cmd/src
|
||||
|
Loading…
Reference in New Issue
Block a user