Sushi: Send parameter values to midi output

I would like to use the sushi internal peak meter plugin and send the parameter via midi.

Is it possible to map some parameter values to midi messages (e.g. a ControlChange message), similar to what is possible to do in the opposite direction (Midi → Paramter)


Sushi lacks any such mapping features internally as far as I know. While hosted plugin parameter changes are communicated from Sushi, that is over OSC and gRPC only.

So, what you are asking for is easily implemented in an external accompanying “backend” / “controller” application that you write and run alongside the Sushi instance, on e.g. your device.

In this specific case, you would enable OSC output for the desired parameters, listen for OSC in e.g. your Python program, and on change, map and output the value to the corresponding MIDI CC, which your Python program should be made to output.

You could also use gRPC instead of OSC if you prefer, but for the specific use-case gRPC might be overkill, and OSC will do nicely.

Ilias of Elk

Thanks Ilias, this makes sense, I will probably implement a small service in golang which should play well with gRPC.

Would it be possible to write a custom plugin that would emit midi messages and map those directly to the midi out of sushi?


That could also work, then you don’t need to instantiate your own MIDI output interface, but use Sushi’s.

But, each plugin has access only to its own parameters of course, and cannot know what goes on in the peakmeter plugin.

Meaning that your plugin would still need to subscribe to any notifications you want from Sushi over OSC/gRPC, and map them to its MIDI output.

Just to add to my answer yesterday - I’m not sure implementing this in a plugin will in the end be saving you all that much effort:

Depending on the plugin format, you may need to do a lot of extra work, to ensure that you do not allocate any memory/cause interruptions in your plugins audio processing callback, when handling incoming gPRC/OSC, and outputting it as midi.

But if it runs as a separate process, you do not need to worry about any such overhead, meaning your implementation is likely much more straight-forward.

Ilias of Elk

Hi laenzi, to add to Ilias’s answer above. Unfortunately not without doing some modifications to sushi if you want output the peak values as CC messages.

You can output midi from a plugin (only VST2 as VST3 doesn’t do midi) and route midi from a plugin to a midi output, but only keyboard messages (note on/off, pressure, modwheel, etc) will be passed through. Reason for this is that sushi will first convert the midi messages to a internal format, and discard things like CC messages.

Thanks Gustav, thats good to know. I dont need to send CC messages, I could also use other messages. I was not aware the VST3 does not do midi :frowning:

Ideally i would like to use CLAP, but since Sushi does not support it, I would then rather fallback to LV2.
Would that work out (based on your experience)?

For simplicity reason (and also from my personal lerning interest point of view) I would rather try to implement a plugin, than having an external service running. It looks a bit cleaner and more reliable to me. The plugin would then also do the measurment algorithm which I would like to tune (eg. the frequency of sent messages…)

It took me a bit time, but now I have some first success with LV2 plugin sending midi data.

I have the fowlloing experimental setup:

Created a plugin that sends note on/off events every 1s. I have tested the plugin both in jalv and in reaper.

When I run it in sushi, however I get the following result:

128:1   Note off                0, note 36
128:1   Note off                0, note 36, velocity 0

test is done by sending the main track (where the plugin is configured) to the midi port and then runningaseqdump -p 128:1

in the logs the folowing is shown:

[2023-04-09 15:33:12.995] [debug] [midi dispatcher] Dispatching midi [80 24 0 0], timestamp: 15726382551
[2023-04-09 15:33:13.996] [debug] [midi dispatcher] Dispatching midi [90 24 0 0], timestamp: 15727382565

intersting, that in the logs it alternates between note on and note of, but also the velocity can not be seen

expected would be to see

100: 80 24 7f note off (channel  0): pitch  36, velocity 127
100: 90 24 7f note on  (channel  0): pitch  36, velocity 127

To me it looks a bit as sushi would some transform the midi messages or maybe misinterpreting the MIDI atom sequences sent by the plugin

I forgot to mention that the sushi version

sushi --version

Version 0.12.0
Build options enabled: vst3, lv2, xenomai, rpc control, ableton link
Audio buffer size in frames: 64
Git commit: 364ebc2f7191fa4a990b5b91e6a430efb991a2db
Built on: 2021-11-03 13:16

@Gustav I have built a debug version of sushi (tag 1.0-rc1_yocto_build since the README of sushi mentiones this as the last version compatible with Elk Audio OS.

Then I used gdb to trace the code related to to the MIDI processing from LV2 plugins.

That exposed the follwoing issues:

a) when converting the midi message from LV2 the last byte is ignored due to this line which decrements the size of the MIDI payload. It might be that this is required for other types of messages, but for Note OFF message this causes the loss of the velocity information.

b) In the internal format KeyboardEvent the range of the velocity field is expected to be 0.0…1.0. However the values are 0…127 as written by the lv2_wrapper and therfore with the encoding on the midi_dispatchter.cpp the velocity value overflows the 7 bit range of the Midi Note OFF message.

As a next step I can propose a fix in a PR.

One thing that is still not so clear: Why is the MIDI channel of the incoming message ignored and instead the channel of the Output Connection is used? What is the rationale behind this?

tested with the patch below, looks promising.

However a real change requires more thoughts. I guess also other message types are affected. Also some unit tests might be needed. But just to document, what I have tried so far:

diff --git a/src/library/lv2/lv2_wrapper.cpp b/src/library/lv2/lv2_wrapper.cpp
index b0ce7078..560983a0 100644
--- a/src/library/lv2/lv2_wrapper.cpp
+++ b/src/library/lv2/lv2_wrapper.cpp
@@ -770,7 +770,7 @@ void LV2_Wrapper::_process_midi_output(Port* port)
         // Get event from LV2 buffer.
         lv2_evbuf_get(buf_i, &midi_frames, &midi_subframes, &midi_type, &midi_size, &midi_body);

-        midi_size--;
+    //    midi_size--;

         if (midi_type == _model->urids().midi_MidiEvent)
@@ -795,7 +795,7 @@ void LV2_Wrapper::_process_midi_output(Port* port)
                                                              0, // Sample offset 0?
-                                                             decoded_message.velocity));
+                                                             static_cast<float>(decoded_message.velocity)/127.0));
                 case midi::MessageType::NOTE_OFF:
@@ -805,7 +805,7 @@ void LV2_Wrapper::_process_midi_output(Port* port)
                                                               0, // Sample offset 0?
-                                                              decoded_message.velocity));
+                                                              static_cast<float>(decoded_message.velocity)/127.0));
                 case midi::MessageType::PITCH_BEND:
1 Like

Hi laenzi, thank you for reporting this, and for documenting a fix!

I will add a bug ticket for this and we should be getting around to prioritizing it in due time. And I agree, this should be accompanied with appropriate unit-test coverage.

While we’ve had VST2 and VST3 support for quite long, LV2 has been the latest addition, and while we did test its MIDI output functionality, this obviously slipped through.

Ilias of Elk

Thank you very much @Ilias for creating a bug.

I was yesterday trying to get the existing unit tests running on (ubuntu 20.04) but so I could not make them pass. Would need to spend more time with the code.

A test (alhtough not really a unit test) could be to use the example fifth, send a midi message and check the resulting midi message.

Not sure if you would consider this as unit test, would be more an integration test. But at least that was my idea to make the bug reproducable.

Hi Laenzi, this goes back to the early days of sushi :slight_smile:

Originally internal note on/off messages in sushi did not have channel information. The idea was that you would use channel information to filter midi messages to different tracks, but once note data was received on a track, it was assumed that channel information was redundant.

Often you would also set a track to omni, where it would receive midi from all channels and essentially not care about the channel info. Though there is no corresponding concept for omni for outgoing midi, outgoing midi messages need to have a channel assigned to them, hence why the channel is assigned by the routing layer and not by the processor itself.

Eventually we added channel information to the internal note messages, meaning that it would be possible to use the channel info coming from the processor. We didn’t change the outgoing behaviour for compatibility reasons though.

@Gustav that is no problem for my use case. I understood now, that I can set the channel with the sushi configuration

 "track_out_connections": [
                 "port": 0,
                "channel": 10, // set the output channel here
                 "track": "main"

this works perfectly for my use case. The velociyt mapping bug is more critical for me. I can work around with patching sushi for now.

Let me know if i should continue to work an PR, but It would require quite some work on my side learn about how to write unit tests for sushi.

1 Like

Great to hear. There is no need for a PR if you can live with patching sushi yourself until the next release. We’ve made a internal ticket for it and it will be fixed in the next release.