Anybody written a Max External for EigenLite API

yeah. for sure, Im sure its personal preference… I think you can learn either way :wink:

anyway, the main point being, Ive made it consistent… not only across eigenharps, but also with EigenD.
of course, how you interpret/use this underlying data is very much up to you.

1 Like

OK - has anybody done any work to derive a “velocity” from the raw incoming data so that I can create MIDI messages for various plugins (most plugins) that won’t accept continuous raw pressure data.

Yes, in fact, I revisited this the other day. :slight_smile: The initial strike will reach a spike after some values, then drop down to the sustained pressure. A hard press will reach the spike after (iirc) around 5-8 values, a soft strike will take longer (perhaps 2x the time?) to reach the top of the spike.

My initial, simple implementation was to wait for the fourth or fifth pressure value and use that as a basis for velocity. It works, but it felt a bit too …twitchy? so I usually had to tweak the velocity response on the synth side to make it feel “musical”.

My new (and in my opinion much improved variant) is that I take the average of value #2 and #3, subtract that from the average of value #6 and #7 to get a base value. I then apply a bezier curve looking like this:

image

My thinking was:

  1. the first value will depend on when the strike hits compared to when the sample is grabbed (if the strike hits right before the sample, value will be close to 0, if the strike hits right after a sample, the first value will be much higher). So I wanted to compensate for this.
  2. Perhaps there is a bit of noise/inaccuracy in the sampling? I don’t know, but since I had the values anyways I took the average of two just in case.
  3. My impression was that pressure didn’t translate linearly to velocity all that well. I wanted to give “normal” key presses quite consistent velocity, and get very low/high velocity only when a strike differs quite a bit from the norm. Hence the curve.

With my limited testing so far (I’ve tried playing some piano VSTs and some random Equator 2 patches on the Pico) this feels much better than my old/simple variant.

The actual implementation is here:
https://github.com/KaiDrange/ECMapper
(ECMapper/Source/Data/MidiGenerator and BezierCurve)

yeah, velocity is a tricky one… as its actually got quite a few variables.

as above, its a number of samples, which you can potentially weight and then apply a curve on .

for MEC, I initially created my own algorithm for it, using a simple weighted average over N samples, but it never felt quite as good as the EigenD version.
so, I later I reimplemented the algorithm used by eigend, which feels pretty good to me.

you can find this here:

I think one thing to bear in mind, is the physical response on the eigen keys is probably not linear, due to its springed nature… I’ve not checked, but I wonder if this is what the eigenD algo is trying to compensate for… i.e. the algo tries to create a more or less linear response, from which you can then apply apply the curve… but Ive not really looked at the maths in depth to check this :wink:

at the end of the day, I dont think there is a ‘correct’ way to do it, rather its just what feels right…
the difficult bit, and where my early attempts failed, are getting a good velocity value for both light touches and heavy/fast strikes… I often found, my attempts would be better on one side of this spectrum than the other.

… I do also think this is why EigenD allows the curve to be altered, you can bias this velocity based on what sounds you are playing, and if they need a heavier/lighter velocity response.

Hmm. I like the thought of having the exact same (default) velocity behaviour across EigenD/MEC/ECMapper (so that if someone creates a synth preset it would feel the same across all solutions). I guess I should consider adding this. At least as an option.

Slightly off-topic, but when I added note-off velocity the other day, I realized I didn’t really know how this should ideally work:
From hard press to fast release = high value
From hard press to slow release = low value
From soft press to slow release = low value
…but what about soft press to fast release? I’m leaning towards low value, so that is what I did. But not at all sure I got that right.

So from a C++ interface/usability perspective, I would like to have something essentially like the following:

class VelocityDetector
{
   public:

      void injectPressureValue(int keyNumber, int latest value);
      std::function<void trigger(int keyNumber, int velocity)> OnNoteOn;
      std::function<void trigger(int keyNumber, int release)> OnNoteOff;

     std::function<void(unsigned long long time, unsigned course, unsigned key, bool active, unsigned pressure, int roll, int yaw)> OnKey;


   public:
      // Other stuff to control/scale/decide what constitutes a legit value
     void sendKeyInfoDuringNoteOn(bool value); // Only callback with key info after we have a note recognized
}

There would essentially be an array of these, one for each possible key

The idea is that the object is reset when key becomes inactive. Once it’s active, pressure values are fed in. When “enough” values are available to calculate the velocity, you trigger the lambda callback.

From a user (developer using EigenLite) perspective, instances of this would be called from the original key method in the EigenLite API and it becomes trivial to just have a “plug and play” replacement

thats pretty much what my voice class in MEC is doing…

however, I also added voicing, as this is almost always needed as well…

anyway, its not something id want to add to EigenLite, as since already mentioned, I want to keep EigenLite as a thin wrapper over the EigenLabs code.

MEC was kind of designed to be the ‘next level up’,
it also has an ‘api’ layer, so doesn’t haven’t to be used as a concrete implementation.

that said, I kind of lost my way a bit with MEC… I started to using it to wrap other devices as well, and that increased the complexity, and its focus…
this came to a head when I abstracted ‘surface layouts’ and started having difficulties about how this would work with different devices.
basically the bi-directional comms is very challenging , it works nicely for going keys → note scales, and with splits… but sending the LED information back was getting very complex.
…so took a break, and never quite got back to it.

on top of that, I also started chucking in support for a ton of other devices, with some general idea of it becoming a performance environment, that brought all my devices together…
and whilst the design is quite clean in the way I did this, its really has lost that all important focus.

so, really, I need to decide what to do with MEC… I need to re-focus it a bit, and perhaps split things off into different projects, to help reduce the complexity.
… and most importantly, focus on a real day to day use-case for my needs.
(if I actively use it, im more likely to develop it !)

Oh I agree - I wasn’t suggesting that — I plan to add it to my app that uses your API

1 Like