Using MIDI controller

I had a quick hack this evening using a Novation Launch Control, and rtmidi. Done very ugly. I basically added some code to main.py which imported rtmidi, and set up a callback. The contents of the callback are rather crude (parameters r,s,t are the three bytes of the MIDI message):
import application
app = application.get_app()
ba = app.brush_adjustment
if r == 0xb8 and s == 0x15:
v = (t/127.0)*5.0-0.5
ba[‘radius_logarithmic’].set_value(v)

For a few movements of the knob (the top left, there are two rows of 8, and 8 buttons), the brush size option moved in sync. Then mypaint crashed out with an X11 error.

What I’m trying to figure out how to do is:

  1. Have the top 8 knobs in this interface control up to 8 of the current tool options, with predefined scaling from [0…127] (a midi byte) to the desired value.
  2. Have the second 8 knobs control the foreground and background colour, and perhaps current layer opacity and something else.
  3. Have the bottom 8 buttons toggle visibility/locking of, say, the bottom layer, and perhaps up to three others.

This is a first attempt at reading the python source of mypaint, and having a go. Wondering as to thoughts…

(Also, more generally, being able to have foot pedals and controllers do stuff would be great, and basically that means merely a capacity to have callbacks fire in response to midi messages, and stuff happen without mypaint crashing.)

@EEight since your post is almost similar to what this person is accomplishing(In terms of using another tool to control MyPaint), do you have any advise on how to proceed?

https://community.mypaint.org/t/liblo-adding-open-sound-control-mobile-interface-to-mp/720?u=odysseywestra

Same goal, almost same technology (osc is an alternative to 7-bit midi), but didn’t have the time to work on this, also I was asking for direction on how to tackle various interface to mypaint: mainly stroke_to, layer, zoom etc. Right now I can control colors, brush, pan, zoom (not smooth).

@Chalisque keep me/us updated…

I’ve started having thoughts about a next stab at this sort of thing. My initial approach had the ‘update stuff’ happen in an input handler function. I did a quick measurement of how many messages per second I could get from twiddling a single knob, and 200ish was not difficult, and my guess is that this is overloading something. If the knob movements are changing e.g. brush size, then there is no need for more updates than one per screen refresh (e.g. about 60 per second). Thus it makes sense to poll for messages once per screen refresh at most, and then store updates in a dict, with newer messages overwriting older ones, and then simply iterate over the dict to actually update things like brush size.

Conceptually this is straightforward. In addition, one could plumb in arbitrary ‘poll queue and store updates in table’ functions, e.g. one for MIDI, one for OSC, one for any other messaging system. It ends up looking like the standard ‘get message/dispatch message’ loop of window systems.

The easy part (polling) is illustrated here: python-rtmidi/examples/basic/midiin_poll.py at master · SpotlightKid/python-rtmidi · GitHub

The hard part (where to plumb this into mypaint) is what I need to figure out. If anybody knows the innards of mypaint well enough to point to events and updates are handled in mypaint, their input would be welcome.

{ DaydreamDump: As an aside, I have been of the mind for years that the event system that does things like keyboard and mouse should be extended to handle generic ‘int valued’ events uniformly, with a means of addressing things by int-valued addresses (think of how IPv4 works: you look up names to numbers, and then work with numbers – rather than OSC making string parsing a necessity). That, and having a PureData/Max patcher-like way of routing and manipulating events. }