Page 1 of 2

lv2 midi input

Posted: Thu Jun 21, 2018 11:07 am
by skei
i'm currently adding lv2 support to my plugin framework.. but the documentation isn't the best i've seen, and i have a few problems.. i started with the midi gate example here: http://lv2plug.in/book/#_midi_gate, and modified it so that it just prints out the raw midi bytes when there's a midi event.. this works in qtractor and ardour, but not in carla.. which is a bit unfortunate, since carla is the only lv2-supporting host i use myself.. with carla, if i print out the supported extensions (the 'features' thing) during instantiation, i don't see any mention of anything resembling midi.. but then, qtractor tells me it only supports one feature ('http://lv2plug.in/ns/ext/urid#map'), but still it works.. hmm.. i'm getting confused..

so, concretely.. what do i do to receive midi from carla?

and also, is there a 'canonical' list of extensions i can expect all hosts to support?

- tor-helge

Re: lv2 midi input

Posted: Fri Jun 22, 2018 4:27 am
by skei
i've been looking through a bunch of examples on github, and trying the event extension stuff, but still the same results..
there's obviously something i don't understand.. :-/ maybe i should put lv2 away for a while, and focus on vst3, and then (possibly) return to it a little later..

Re: lv2 midi input

Posted: Fri Jun 22, 2018 7:10 am
by ventosus
this works in qtractor and ardour, but not in carla..


Some hosts route/eat/drop standard output/error from plugins. Don't know whether carla is one of them. You can try to write to a file instead...

Or even better, use LV2's log extension (if carla supports it).

so, concretely.. what do i do to receive midi from carla?


You just mention in your plugin metadata (the strange RDF turtle file) that your atom event port support MIDI, and the host will send the MIDI to that port.

and also, is there a 'canonical' list of extensions i can expect all hosts to support?


No, that's why they are called extensions. It works like this: If a plugin needs an extension, it mentions that in its plugin metadata and if the host does not support that extension, it simply won't support that plugin, e.g. don't show it as a selection to the user.

Re: lv2 midi input

Posted: Fri Jun 22, 2018 7:11 am
by ventosus
Here a MIDI-only plugin bundle with lots of examples:

https://github.com/x42/midifilter.lv2

Re: lv2 midi input

Posted: Fri Jun 22, 2018 7:15 am
by ventosus
qtractor tells me it only supports one feature ('http://lv2plug.in/ns/ext/urid#map')


MIDI plugins (or more general event plugins) need that extension. So the plugin you've looked at lists it as required in its plugin metadata.

Some hosts list all their supported extensions in the features list. Other hosts only list the required (by the plugin) extensions in the features list. The latter is most probably what you saw in qtractor (I'm pretty sure that it supports more extensions...)

Re: lv2 midi input

Posted: Fri Jun 22, 2018 7:29 am
by skei
ventosus wrote:Some hosts route/eat/drop standard output/error from plugins. Don't know whether carla is one of them. You can try to write to a file instead...
Or even better, use LV2's log extension (if carla supports it).


i know.. (and yes, carla drops stdout, it seems).. in my framework, i have a bunch of "debug outputs", selectable with some #ifdefs, like stdout, logfile, socket, gui/window, etc.. when coding plugins, i usually push all debug output through a socket, and print it out in another terminal.. all other debug info is printed correctly, so this works quite well..

ventosus wrote:You just mention in your plugin metadata (the strange RDF turtle file) that your atom event port support MIDI, and the host will send the MIDI to that port.


i thought i did.. i followed the examples rigorously, and double- and triple-checked everything.. but obviously there's something i missed, or whatever..

ventosus wrote:If a plugin needs an extension, it mentions that in its plugin metadata and if the host does not support that extension, it simply won't support that plugin, e.g. don't show it as a selection to the user.


yeah, i have understood that now..

Re: lv2 midi input

Posted: Fri Jun 22, 2018 7:31 am
by skei
ventosus wrote:Here a MIDI-only plugin bundle with lots of examples:

https://github.com/x42/midifilter.lv2


thanks!

Re: lv2 midi input

Posted: Fri Jun 22, 2018 7:35 am
by skei
ventosus wrote:
qtractor tells me it only supports one feature ('http://lv2plug.in/ns/ext/urid#map')


MIDI plugins (or more general event plugins) need that extension. So the plugin you've looked at lists it as required in its plugin metadata.

Some hosts list all their supported extensions in the features list. Other hosts only list the required (by the plugin) extensions in the features list. The latter is most probably what you saw in qtractor (I'm pretty sure that it supports more extensions...)


ok..

Re: lv2 midi input

Posted: Fri Jun 22, 2018 10:18 am
by skei
falkTX wrote:Carla captures the stdout/stderr so it can be shown on the GUI, it is useful to get some debug output out of plugins from users that do not start Carla from a terminal.
Disabling this is quite easy, it is right on the first page of Carla settings.


yeah.. i see it now.. :-) no idea why i overlooked that before..
but since i have this socket thing up and running (i initially needed it to test and debug vst plugins in bitwig), it's not an issue, actually..

Re: lv2 midi input

Posted: Sat Jun 23, 2018 4:43 pm
by skei
so, the problem i'm having is getting midi input from carla.. the code below works in qtractor and ardour (haven't tested much else).. in carla, the light/led lights up when i press a midi key, but my plugin doesn't seem to receive the midi events..

i tried to extract the minimal code needed to show what i do.. let's hope it still understandable :?

i separate plugins into two classes, the main plugin class which handles/manages the plugin/.so itself, and the descriptors, etc, and an instance class for each, well, instance.. the plugin class is a static class (object?), mainly to have automatic cleanup via the destructor when the .so is unloaded.. MLV2* prefixed variables are part of the plugin (or instance) class, on_instance_midiEvent() is a virtual function that individual plugins override if they want midi input.. and also, most of this is wrapped in #ifdef PLUGIN_RECEIVE MIDI .. #endif and similar constructs..

define midi input port:

Code: Select all

lv2:port [
   a ev:EventPort, lv2:InputPort;
   lv2:index         6 ;
   ev:supportsEvent   <http://lv2plug.in/ns/ext/midi#MidiEvent> ;
   lv2:symbol         "midi" ;
   lv2:name         "MIDI Input" ;
] .


instantiation:

Code: Select all

LV2_URI_Map_Feature* map_feature;
const LV2_Feature* const*  ft;
for (ft=features; *ft; ft++) {
   if (!strcmp((*ft)->URI,"http://lv2plug.in/ns/ext/uri-map")) { // LV2_URI_MAP_URI
      map_feature = (LV2_URI_Map_Feature*)(*ft)->data;
      MLV2EventType = map_feature->uri_to_id(
         map_feature->callback_data,
         "http://lv2plug.in/ns/ext/event",
         "http://lv2plug.in/ns/ext/midi#MidiEvent"
      );
   }
   else if (!strcmp((*ft)->URI, "http://lv2plug.in/ns/ext/event")) {
      MLV2EventFeature =(LV2_Event_Feature*)(*ft)->data;
   }
}


connect the port:

Code: Select all

MLV2EventBuffer = (LV2_Event_Buffer*)data_location;


and finally, at the start of the run() function:

Code: Select all

LV2_Event_Iterator event_iterator;
lv2_event_begin(&event_iterator,MLV2EventBuffer);
while (lv2_event_is_valid(&event_iterator)) {
   uint8_t* data;
   LV2_Event* event = lv2_event_get(&event_iterator,&data);
   if (event->type == 0) {
      MLV2EventFeature->lv2_event_unref(MLV2EventFeature->callback_data, event);
   }
   else if (event->type == MLV2EventType) {
      uint32_t offset = event->frames;
      const uint8_t* msg = (uint8_t*)data;
      on_instance_midiEvent(offset,msg[0],msg[1],msg[2]);
   }
   lv2_event_increment(&event_iterator);
}


does anybody see any obvious errors or missing things or other issues?

i also tried another way, using some AtomPort stuff (i think?), with the same results,
except that i felt it was more confusing - more terminology and stuff i don't fully grasp yet..
(but that also worked in qtractor and ardour, but not carla, so the problem might be somewhere else?)

Re: lv2 midi input

Posted: Sat Jun 23, 2018 6:42 pm
by skei
falkTX wrote:do you have more than 1 midi input port?


no, just one.. this is just a test plugin, and i have 2 audio inputs, 2 audio outputs, and 2 control inputs (parameters), plus this midi input

falkTX wrote:try setting the lv2:designation lv2:control on the port.


i will try that

falkTX wrote:you are using 2 deprecated things, which is not a good thing to start with.
atom can be confusing, but please use urid-map instead of uri-map.
the 2nd parameter on the uri-map function is confusing..


ok.. i have to read up a little about these atoms, and look at urid-map

falkTX wrote:is the full code published somewhere?


not yet.. i will publish the library/framework as soon as i can get the main plugin formats (ladspa, dssi, lv2, vst2, vst3, standalone) basically working (at least a minimal version, so i can build for example a simple gui-less synth in all formats)

..and decided on licensing and all that stuff :-/

falkTX wrote:also, is your carla self-compiled? I would say, make sure you are using latest git, I am about to make a new bugfix release.
lots of stuff fixed lately


i use the latest version from your kx studio repositories (help/about says "version 1.9.8 (2.0-beta6)"
(in linux mint/cinnamon, in case that matters)..

always looking forward to new versions of your stuff..
(mega-thanks for all your linux-audio efforts!)

Re: lv2 midi input

Posted: Sat Jun 23, 2018 7:33 pm
by skei
i looked at the example from here: http://lv2plug.in/book/#_midi_gate
and came up with something like the below.. i don't fully understand what everyting is doing yet, so i absolutely need to look at some docs :-)

but, same result.. works in qtractor, didn't test in ardour, doesn't work in carla..

the little led/light on the plugin in the carla rack view lights up when i press a midi key.. does that mean carla is sending the midi to the plugin?

kode_debug.ttl:

Code: Select all

@prefix doap:      <http://usefulinc.com/ns/doap#> .
@prefix lv2:      <http://lv2plug.in/ns/lv2core#> .
@prefix rdf:      <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:      <http://www.w3.org/2000/01/rdf-schema#> .
@prefix atom:      <http://lv2plug.in/ns/ext/atom#> .
@prefix midi:      <http://lv2plug.in/ns/ext/midi#> .
@prefix urid:      <http://lv2plug.in/ns/ext/urid#> .

<urn:skei.audio/kode_debug>
   a lv2:Plugin , lv2:InstrumentPlugin ;
   doap:name         "kode_debug" ;
   lv2:optionalFeature   lv2:hardRTCapable ;
   lv2:requiredFeature   urid:map ;
   lv2:port [
      ...
      ...
   ] , [
      a lv2:InputPort , atom:AtomPort ;
      atom:bufferType      atom:Sequence ;
      atom:supports      midi:MidiEvent ;
      lv2:designation      lv2:control ;
      lv2:index         6 ;
      lv2:symbol         "midi_in" ;
      lv2:name         "Midi in"
   ] .


Code: Select all

#define KODE_PLUGIN_LV2_MAX_URIS 16

plugin instance class:

   const LV2_Atom_Sequence*   MLV2AtomSequence   = KODE_NULL;
   LV2_URID_Map*            MLV2Map            = KODE_NULL;
   KODE_uint32               MMidiEventType      = 0;
   LV2_URID*                MLV2URIS         = KODE_NULL;
      
plugin class.

   // for some reason, i thought it could be smart to prepare for multiple uri's..
   LV2_URID   MLV2URIS[KODE_PLUGIN_LV2_MAX_URIS]   = {0};
      
instantiate()

   LV2_URID_Map* map = NULL;
   for (int i=0; features[i]; ++i) {
      if (!strcmp(features[i]->URI, LV2_URID__map)) {
         map = (LV2_URID_Map*)features[i]->data;
         break;
      }
   }
   if (!map) return KODE_NULL;
   MLV2URIS[0] = map->map(map->handle, LV2_MIDI__MidiEvent);
   ...
   instance->MLV2URIS = MLV2URIS;   

connect()

   MLV2AtomSequence = (const LV2_Atom_Sequence*)data_location;

run()

   uint32_t offset = 0;
   LV2_ATOM_SEQUENCE_FOREACH(MLV2AtomSequence, ev) {
      if (ev->body.type == MLV2URIS[0]) {
         const uint8_t* const msg = (const uint8_t*)(ev + 1);
         offset = (uint32_t)ev->time.frames;
         on_instance_midiEvent(offset,msg[0],msg[1],msg[2]);
      }
   }

Re: lv2 midi input

Posted: Sat Jun 23, 2018 9:16 pm
by skei
falkTX wrote:are you trying to send MIDI CCs by any chance?
those are disabled by default.

btw, when plugins have midi input, you can use the built-in keyboard widget to send some notes to the plugin.
test that too, to see if something gets received or not.


no, i'm sending/playing notes..
and, nothing happens if i click on the on-screen keyboard..
it looks like everything is ok on the carla side, but no reaction from my plugin..
so, i just tested with jalv (jalv.select?)..
the plugin loads and everything, but just like carla, nothing is printed out..
(and it looks ok in catia, with a red midi_in port)

maybe that indicates that it's the handling of events insinde my plugin that is the problem?
hmmm...

i just watched a video about the atom stuff, and read some docs, or specs?.. something are a little bit clearer (atom, uri/urid, at least the principles, the 'what' part).. but something are maybe a bit more confusing (the 'how') part)..

it wouldn't hurt if there were more 'how to get started with..' or 'introduction to..' tutorials.. :-)

Re: lv2 midi input

Posted: Sun Jun 24, 2018 6:42 am
by skei
i feel so stupid! my connect_port function was buggy! :oops:
after fixing this, i have midi input in carla too!!
sorry for taking up everybody's time with my stupidity!

Re: lv2 midi input

Posted: Sun Jun 24, 2018 10:49 am
by skei
yeah.. but at least i got rid of the deprecated stuff.. :)