lv2 midi in
This commit is contained in:
parent
d8379dcb6d
commit
8b1345c662
4
TODO
4
TODO
@ -13,3 +13,7 @@
|
||||
* more mappings, lv2 port props
|
||||
* latency mechanism, see https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Workflow+Diagrams/Get+Latency+Call+Sequence.html, https://steinbergmedia.github.io/vst3_doc/vstinterfaces/classSteinberg_1_1Vst_1_1IAudioProcessor.html#af8884671ccefe68e0a86e72413a0fcf8, https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Workflow+Diagrams/Audio+Processor+Call+Sequence.html
|
||||
* vst3 don't include/link libm if not needed, etc. (reduce to minimum reqs)
|
||||
* proper parameter automation
|
||||
* proper sample accurate midi (see https://lv2plug.in/book/#_midi_gate)
|
||||
* whole midi properly (see https://github.com/harryhaaren/lv2/blob/master/lv2/lv2plug.in/ns/ext/midi/midi.h, https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message, http://midi.teragonaudio.com/tech/midispec.htm)
|
||||
* uneven midi pitch bend, what to do?
|
||||
|
@ -20,6 +20,10 @@
|
||||
] ;
|
||||
lv2:minorVersion {{=/^([0-9]+)\./.exec(it.lv2.version)[1]}} ;
|
||||
lv2:microVersion {{=/^[0-9]+\.([0-9]+)/.exec(it.lv2.version)[1]}} ;
|
||||
{{?it.tibia.lv2.ports.find(p => p.type == "midi")}}
|
||||
lv2:requiredFeature urid:map ;
|
||||
lv2:optionalFeature log:log ;
|
||||
{{?}}
|
||||
lv2:optionalFeature lv2:hardRTCapable ;
|
||||
lv2:port [
|
||||
{{~it.tibia.lv2.ports :p:i}}
|
||||
|
BIN
templates/lv2/src/.lv2.c.swp
Normal file
BIN
templates/lv2/src/.lv2.c.swp
Normal file
Binary file not shown.
@ -7,6 +7,12 @@
|
||||
#define DATA_PRODUCT_CONTROL_INPUTS_N {{=it.product.parameters.filter(x => x.direction == "input").length}}
|
||||
#define DATA_PRODUCT_CONTROL_OUTPUTS_N {{=it.product.parameters.filter(x => x.direction == "output").length}}
|
||||
|
||||
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
|
||||
static uint32_t midi_in_index[DATA_PRODUCT_MIDI_INPUTS_N] = {
|
||||
{{~it.tibia.lv2.ports.filter(x => x.type == "midi" && x.direction == "input") :p}}{{=p.busIndex}}, {{~}}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
|
||||
|
||||
# define DATA_PARAM_BYPASS 1
|
||||
|
@ -1,36 +1,77 @@
|
||||
#include "lv2/core/lv2.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "data.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#include "lv2/core/lv2.h"
|
||||
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUTS_N > 0
|
||||
#include "lv2/core/lv2_util.h"
|
||||
#include "lv2/atom/util.h"
|
||||
#include "lv2/atom/atom.h"
|
||||
#include "lv2/log/log.h"
|
||||
#include "lv2/log/logger.h"
|
||||
#include "lv2/midi/midi.h"
|
||||
#include "lv2/urid/urid.h"
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#include <xmmintrin.h>
|
||||
#include <pmmintrin.h>
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
plugin p;
|
||||
plugin p;
|
||||
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
|
||||
const float * x[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N];
|
||||
const float * x[DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N];
|
||||
#endif
|
||||
#if DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N > 0
|
||||
float * y[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N];
|
||||
float * y[DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N];
|
||||
#endif
|
||||
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
|
||||
const LV2_Atom_Sequence * x_midi[DATA_PRODUCT_MIDI_INPUTS_N];
|
||||
#endif
|
||||
#if DATA_PRODUCT_MIDI_OUTPUTS_N > 0
|
||||
LV2_Atom_Sequence * y_midi[DATA_PRODUCT_MIDI_OUTPUTS_N];
|
||||
#endif
|
||||
#if (DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N) > 0
|
||||
float * c[DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N];
|
||||
float * c[DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N];
|
||||
#endif
|
||||
#if DATA_PRODUCT_CONTROL_INPUTS_N > 0
|
||||
float params[DATA_PRODUCT_CONTROL_INPUTS_N];
|
||||
float params[DATA_PRODUCT_CONTROL_INPUTS_N];
|
||||
#endif
|
||||
void * mem;
|
||||
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUT_N > 0
|
||||
LV2_URID_Map * map;
|
||||
LV2_Log_Logger logger;
|
||||
LV2_URID uri_midi_MidiEvent;
|
||||
#endif
|
||||
void * mem;
|
||||
} plugin_instance;
|
||||
|
||||
static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double sample_rate, const char * bundle_path, const LV2_Feature * const * features) {
|
||||
plugin_instance *instance = malloc(sizeof(plugin_instance));
|
||||
if (instance == NULL)
|
||||
return NULL;
|
||||
|
||||
#if DATA_PRODUCT_MIDI_INPUTS_N + DATA_PRODUCT_MIDI_OUTPUT_N > 0
|
||||
// from https://lv2plug.in/book
|
||||
const char* missing = lv2_features_query(features,
|
||||
LV2_LOG__log, &instance->logger.log, false,
|
||||
LV2_URID__map, &instance->map, true,
|
||||
NULL);
|
||||
|
||||
lv2_log_logger_set_map(&instance->logger, instance->map);
|
||||
if (missing) {
|
||||
lv2_log_error(&instance->logger, "Missing feature <%s>\n", missing);
|
||||
free(instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
instance->uri_midi_MidiEvent = instance->map->map(instance->map->handle, LV2_MIDI__MidiEvent);
|
||||
#endif
|
||||
|
||||
plugin_init(&instance->p);
|
||||
|
||||
plugin_set_sample_rate(&instance->p, sample_rate);
|
||||
size_t req = plugin_mem_req(&instance->p);
|
||||
if (req != 0) {
|
||||
@ -42,6 +83,7 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
|
||||
plugin_mem_set(&instance->p, instance->mem);
|
||||
} else
|
||||
instance->mem = NULL;
|
||||
|
||||
#if DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N > 0
|
||||
for (uint32_t i = 0; i < DATA_PRODUCT_AUDIO_INPUT_CHANNELS_N; i++)
|
||||
instance->x[i] = NULL;
|
||||
@ -50,10 +92,19 @@ static LV2_Handle instantiate(const struct LV2_Descriptor * descriptor, double s
|
||||
for (uint32_t i = 0; i < DATA_PRODUCT_AUDIO_OUTPUT_CHANNELS_N; i++)
|
||||
instance->y[i] = NULL;
|
||||
#endif
|
||||
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
|
||||
for (uint32_t i = 0; i < DATA_PRODUCT_MIDI_INPUTS_N; i++)
|
||||
instance->x_midi[i] = NULL;
|
||||
#endif
|
||||
#if DATA_PRODUCT_MIDI_OUTPUTS_N > 0
|
||||
for (uint32_t i = 0; i < DATA_PRODUCT_MIDI_OUTPUTS_N; i++)
|
||||
instance->y_midi[i] = NULL;
|
||||
#endif
|
||||
#if (DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N) > 0
|
||||
for (uint32_t i = 0; i < DATA_PRODUCT_CONTROL_INPUTS_N + DATA_PRODUCT_CONTROL_OUTPUTS_N; i++)
|
||||
instance->c[i] = NULL;
|
||||
#endif
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@ -75,14 +126,14 @@ static void connect_port(LV2_Handle instance, uint32_t port, void * data_locatio
|
||||
#endif
|
||||
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
|
||||
if (port < DATA_PRODUCT_MIDI_INPUTS_N) {
|
||||
// TBD
|
||||
i->x_midi[port] = data_location;
|
||||
return;
|
||||
}
|
||||
port -= DATA_PRODUCT_MIDI_INPUTS_N;
|
||||
#endif
|
||||
#if DATA_PRODUCT_MIDI_OUTPUTS_N > 0
|
||||
if (port < DATA_PRODUCT_MIDI_OUTPUTS_N) {
|
||||
// TBD
|
||||
i->y_midi[port] = data_location;
|
||||
return;
|
||||
}
|
||||
port -= DATA_PRODUCT_MIDI_OUTPUTS_N;
|
||||
@ -146,6 +197,47 @@ static void run(LV2_Handle instance, uint32_t sample_count) {
|
||||
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
|
||||
#endif
|
||||
|
||||
// from https://lv2plug.in/book
|
||||
#if DATA_PRODUCT_MIDI_INPUTS_N > 0
|
||||
for (size_t j = 0; j < DATA_PRODUCT_MIDI_INPUTS_N; j++)
|
||||
LV2_ATOM_SEQUENCE_FOREACH(i->x_midi[j], ev) {
|
||||
if (ev->body.type == i->uri_midi_MidiEvent) {
|
||||
const uint8_t * const msg = (const uint8_t *)(ev + 1);
|
||||
switch (lv2_midi_message_type(msg)) {
|
||||
case LV2_MIDI_MSG_NOTE_ON:
|
||||
if (msg[2] == 0)
|
||||
plugin_note_off(&i->p, midi_in_index[j], msg[1], 64.f / 127.f);
|
||||
else
|
||||
plugin_note_on(&i->p, midi_in_index[j], msg[1], (1.f / 127.f) * msg[2]);
|
||||
break;
|
||||
case LV2_MIDI_MSG_NOTE_OFF:
|
||||
plugin_note_off(&i->p, midi_in_index[j], msg[1], (1.f / 127.f) * msg[2]);
|
||||
break;
|
||||
case LV2_MIDI_MSG_CONTROLLER:
|
||||
switch (msg[1]) {
|
||||
case LV2_MIDI_CTL_ALL_SOUNDS_OFF:
|
||||
plugin_all_sounds_off(&i->p, midi_in_index[j]);
|
||||
break;
|
||||
case LV2_MIDI_CTL_ALL_NOTES_OFF:
|
||||
plugin_all_notes_off(&i->p, midi_in_index[j]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case LV2_MIDI_MSG_CHANNEL_PRESSURE:
|
||||
plugin_channel_pressure(&i->p, midi_in_index[j], (1.f / 127.f) * msg[1]);
|
||||
break;
|
||||
case LV2_MIDI_MSG_BENDER:
|
||||
plugin_pitch_bend_change(&i->p, midi_in_index[j], (1.f / 8192.f) * (msg[2] << 7 | msg[1]) - 1.f);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
plugin_process(&i->p, i->x, i->y, sample_count);
|
||||
|
||||
#if defined(__aarch64__)
|
||||
|
@ -7,12 +7,14 @@ module.exports = function (data, api) {
|
||||
{ id: "atom", uri: "http://lv2plug.in/ns/ext/atom#" },
|
||||
{ id: "doap", uri: "http://usefulinc.com/ns/doap#" },
|
||||
{ id: "foaf", uri: "http://xmlns.com/foaf/0.1/" },
|
||||
{ id: "log", uri: "http://lv2plug.in/ns/ext/log#" },
|
||||
{ id: "lv2", uri: "http://lv2plug.in/ns/lv2core#" },
|
||||
{ id: "midi", uri: "http://lv2plug.in/ns/ext/midi#" },
|
||||
{ id: "pprops", uri: "http://lv2plug.in/ns/ext/port-props#" },
|
||||
{ id: "rdf", uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#" },
|
||||
{ id: "rdfs", uri: "http://www.w3.org/2000/01/rdf-schema#" },
|
||||
{ id: "units", uri: "http://lv2plug.in/ns/extensions/units#" }
|
||||
{ id: "units", uri: "http://lv2plug.in/ns/extensions/units#" },
|
||||
{ id: "urid", uri: "http://lv2plug.in/ns/ext/urid#" }
|
||||
],
|
||||
units: {
|
||||
"bar": "@units:bar",
|
||||
@ -64,14 +66,14 @@ module.exports = function (data, api) {
|
||||
for (; bi < audioBuses.length; bi++) {
|
||||
var b = audioBuses[bi];
|
||||
if (b.channels == "mono") {
|
||||
var e = { type: "audio", direction: b.direction, name: b.name, sidechain: b.sidechain, cv: b.cv, optional: b.optional };
|
||||
var e = { type: "audio", direction: b.direction, name: b.name, sidechain: b.sidechain, cv: b.cv, optional: b.optional, busIndex: bi };
|
||||
e.symbol = data.lv2.busSymbols[bi];
|
||||
ports.push(e);
|
||||
} else {
|
||||
var e = { type: "audio", direction: b.direction, name: b.name + " Left", shortName: b.shortName + " L", sidechain: b.sidechain, cv: b.cv };
|
||||
var e = { type: "audio", direction: b.direction, name: b.name + " Left", shortName: b.shortName + " L", sidechain: b.sidechain, cv: b.cv, busIndex: bi };
|
||||
e.symbol = data.lv2.busSymbols[bi] + "_L";
|
||||
data.tibia.lv2.ports.push(e);
|
||||
var e = { type: "audio", direction: b.direction, name: b.name + " Right", shortName: b.shortName + " R", sidechain: b.sidechain, cv: b.cv };
|
||||
var e = { type: "audio", direction: b.direction, name: b.name + " Right", shortName: b.shortName + " R", sidechain: b.sidechain, cv: b.cv, busIndex: bi };
|
||||
e.symbol = data.lv2.busSymbols[bi] + "_R";
|
||||
ports.push(e);
|
||||
}
|
||||
@ -83,7 +85,7 @@ module.exports = function (data, api) {
|
||||
var ports = [];
|
||||
for (var i = 0; i < midiBuses.length; i++, bi++) {
|
||||
var b = midiBuses[i];
|
||||
var e = { type: "midi", direction: b.direction, name: b.name, sidechain: b.sidechain, control: b.control, optional: b.optional };
|
||||
var e = { type: "midi", direction: b.direction, name: b.name, sidechain: b.sidechain, control: b.control, optional: b.optional, busIndex: bi };
|
||||
e.symbol = data.lv2.busSymbols[bi];
|
||||
ports.push(e);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ typedef struct plugin {
|
||||
float * delay_line;
|
||||
size_t delay_line_cur;
|
||||
float z1;
|
||||
float cutoff_k;
|
||||
} plugin;
|
||||
|
||||
static void plugin_init(plugin *instance) {
|
||||
@ -38,6 +39,7 @@ static void plugin_reset(plugin *instance) {
|
||||
memset(instance->delay_line, 0, instance->delay_line_length * sizeof(float));
|
||||
instance->delay_line_cur = 0;
|
||||
instance->z1 = 0.f;
|
||||
instance->cutoff_k = 1.f;
|
||||
}
|
||||
|
||||
static void plugin_set_parameter(plugin *instance, size_t index, float value) {
|
||||
@ -68,7 +70,7 @@ 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);
|
||||
const float mA1 = instance->sample_rate / (instance->sample_rate + 6.283185307179586f * instance->cutoff);
|
||||
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];
|
||||
const float x = instance->delay_line[calc_index(instance->delay_line_cur, delay, instance->delay_line_length)];
|
||||
@ -80,3 +82,22 @@ static void plugin_process(plugin *instance, const float **inputs, float **outpu
|
||||
outputs[0][i] = instance->bypass ? inputs[0][i] : instance->gain * y;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
static void plugin_note_off(plugin *instance, size_t index, uint8_t note, float velocity) {
|
||||
}
|
||||
|
||||
static void plugin_all_sounds_off(plugin *instance, size_t index) {
|
||||
}
|
||||
|
||||
static void plugin_all_notes_off(plugin *instance, size_t index) {
|
||||
}
|
||||
|
||||
static void plugin_channel_pressure(plugin *instance, size_t index, float value) {
|
||||
}
|
||||
|
||||
static void plugin_pitch_bend_change(plugin *instance, size_t index, float value) {
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user