Working MIDI devices

It does work.

1 Like

Thanks!
Can you also use multiple midi controler, via the USB-A midi host, with the Meta?

usb hubs

no support at this time

(usb) midi hubs

hmm, I cannot remember status on this one.
Im pretty sure you cannot target/select a particular ā€˜port’ , as discussed in this topic.
I’ve a suspicion it may merge the inputs of all ports, and only send to the first port?

that said, I may have missed progress on this one… @danngreen ?

Id hope we can get full support (input and output) on midi hubs, even if it’s initially just at the sdk level, as per my feedback on sdk 2.1.

this would be really useful for some modules Im working on for MPE , as an MPE device uses all 16 channels, so plug one in, and thats all you get.
(ok, you could use global channel for other uses ;))

1 Like

I started a branch for USB hubs, but didn’t get it stable enough. I could give it another go sometime, but I think ā€œMIDI hubsā€ (aka USB-MIDI cables), is probably going to offer more benefit. Also it should be easier to add since it’s not at the driver level (I’m not sure why I tried to do USB hubs first, I thought it would be easier than it was)

I’m still thinking to add an API for a MIDI browser similar to the File Browser. For backwards compatibility you could choose one as the default in the main settings/prefs. Then there would be an api to let the user select a MIDI device/cable id, in case a module wants to offer that (or if a module can connect to multiple devices).

2 Likes

Thank you @danngreen, the API would be great and very useful!

If the H4MIDI appears to the host as a single USB-MIDI device with one or more endpoints, would the META be able to communicate with it under the current implementation?
Or should I wait for the MIDI Browser API to be implemented for multi-port devices?
This will help me know whether I can integrate the H4MIDI now, or whether I should plan for future firmware support.
Thanks again for the constant updates on META — the module is absolutely fantastic ! :folded_hands:

yeah, per previous topic, using the usb cable id approach hopefully shouldn’t be too much of an upheaval.

these cables/virtual ports are also used for things other than midi hubs, many (usb) midi devices also use them.
e.g Haken, Osmose, Embodme, Push, Electra One, Squarp Hapax… are just the ones sitting on my desk :wink:
and for some of these , which (virtual) port you use is very important - Haken/Osmose you must send midi data on port 2 (not port 1).

it be nice to have a browser / selector, but thats more a nice to have than required - Im so used to devices not show the device name, Im used to select ports purely by their number.

With the H4MIDI you can connect up to 8 USB-Midi devices directly to the MM. Perhaps need to power the H4MIDI.

1 Like

Hi,
You made it work with more then 2 devices on the midi hub with META or are you relating to the general specs of the H4MIDI?

Ok I’m doing this now. I realized backwards compatibility is going to be an issue here, since this changes the size of the midi message. But I think I can squeeze the cable number in the top 4 bits of the rack::midi::Message::size byte, since that can only be 0-3 anyways.

You should be able to use it like:

rack::midi::InputQueue midiInput;

void read_midi() {
  rack::midi::Message msg;
  if (midiInput.tryPop(&msg, args.frame)) {
     if (msg.usb_cable == 2) // respond to cable 2
      ...

So the data is getting passed to modules, but it’s still ā€œblindā€ – there’s no way to know if the attached device has a cable 2, etc.

In lieu of a MIDI browser, I could just add some API calls like int get_num_usb_midi_in_jacks() and std::string get_usb_midi_in_jack_name(int id) so you could at least check if your desired midi device is attached and/or if a particular jack/cable id is valid.
And I need to do the same for MIDI outputs now, too…

1 Like

sounds, like a good solution - midi is all bit masking :laughing:

descriptors would be useful,
that said, I found often the descriptors themselves are little more than port 1 / port 2. - but still better than nothing.

(probably better for a different topic, but …)
I wonder , how does VCV handle this? does it just generate a different midi device for each port?

EDIT: yeah, vcv just creates a Midi Device for each port, so I get ā€œOsmose - Port 1ā€, ā€œOsmose - Port 2ā€

you may want to bare this in mind for the ā€˜mid term’, if you plan to add this as part of VCV Wrapper ā€˜compatibility’ - but its not something, I worry about at present, the midi stuff is currently MM specific when I use it.

btw: while you are in this ā€˜area’, did you look at the sysex suggestions I made on the other topic, again following the usb midi protocol, which uses (multiple) fixed 4 byte packets.
Ive a couple of use-cases I’d like to mess with in this area - in particular the way Embody Erae Touch has a (sysex) API to control ā€˜custom surfaces’ - could be fun with the MM to give a dynamic surface to play with e.g. a physical sequence surface with bi-directional feedback.

Yeah, right now I’m just pausing the MIDI RX when it sees an 0xF0 in the status byte, and then resumes when it sees an 0xF7 in any status or data bytes (but not the usb header). I’m a little unsure if in practice actual devices use the USB MIDI Sysex start/stop and packet size indicators…

So I could turn this filter off easily, and it would work if all packets in the SysEx field contained a USB header conforming to the spec (the Table 4-1 on p16 of https://www.usb.org/sites/default/files/midi10.pdf), that is, if all packets in the Sysex stream had a CIN set to 0x4-0x7.

But it won’t work if just raw data is sent as 4 x bytes per packet (using 0xF7 anywhere to mark the end of the stream). That is, the raw data needs to be split up into 3-byte packets, with a USB MIDI header (CN/CIN) pre-pended. I’m not sure if any MIDI devices do this? It would mean losing the concept of USB ā€œcable/jackā€ since that header would be hijacked for the raw data.

yeah, so the way it works is…
you get 0x4 to start, then all subsequent message are 0x4 (hence the start and continue) , until you get 0x5-7 which indicates the end. - put another way it was a usb midi ā€œhackā€, to say at the end of the message how many bytes are valid.
its a very simple system :wink:

the key here is… the CIN always determines the message… since you can actually receive other messages (e.g. note on/off/cc) whilst sysex is active. hence you cannot just have ā€˜raw bytes’, the CIN always indicates message type including still part of the sysex
note: you can only have one sysex active at at time.

its all quite transparent… I think last time I read up on this was when doing sysex for axoloti :slight_smile:

Ok, excellent, that’s how I understood it too. Then I won’t have to hack it so hard to not break backwards API compatibility. I should be able to just turn off the SysEx filter and dump the 1-3 byte packets into the midi message data.
I might have to use another bit to indicate this is a sysex packet so a module can query if the data is sysex.

1 Like

I don’t have the H4MIDI but scrolling through the docs, it looks like you can configure it extensively in terms of how it merges multiple streams.

So the current MM firmware would work with it if you did something like routing each device to a different channel. Then you could have various MIDI modules in your patch listening in different channels.
Or configure it to keep the channels intact but just merge all the streams together (the MM wouldn’t be able to tell which source a given message came from, but it would get all the messages including channel data OK)

1 Like

cool, I’ll keep an eye out for a test firmware.
I’ve got a a test vcv module(somewhere :laughing: ) I wrote for the Erae Touch which uses sysex that I can test with.
iirc, it’ll also need to use the 2nd midi port, so will test that at the same time.


note: vcv does not support sysex, so my module had to connect directly to the device, rather than use the vcv api - so hopefully can adapt that fairly easily.
I’d expect , like this module, modules using sysex will be quite specific to devices - nature of the beast.

This doesn’t quite conform to the midi spec. If any non-realitime status byte is received while a port is receiving sysex then that effectively ends the message.

Here is the relevant quote from the midi-spec

interesting point… but is that not superseded by usb midi spec?
i.e. you could make it such that usb-usb midi would allow for interleaving but still allow for backwards compatibility for traditional serial devices.
(bare in mind any din ↔ usb midi converter is already having to do some translation, so would not be ā€˜difficult’ to do this)

usb midi spec, basically has extra redundancy that the serial midi did not have, which is what (could) allow for interleaving.

all that said, Id have to dig thru the usb midi spec to look for where it says this is possible - I could also check it out in practice to see.

I implemented this to what I think is the standard-compliant, and just passing non-compliant situations to module to deal with if it cares to. In the way I did it for incoming MIDI, the situation @audiobird you’re bringing up (a USB MIDI device ending a SysEx stream with an 0xF7 in the payload, without setting CIN to 0x5, 0x6 or 0x7) would need to be caught by the module that’s interpreting the sysex data. In the way I have it, the module just gets a struct with this:

struct rack::Message {
	std::array<uint8_t, 3> bytes{};
	uint8_t usb_cable : 4 = 0;
	uint8_t usb_code : 4 = 0;
///...
	int getUsbCable() const {
		return usb_cable;
	}

	int getUsbCIN() const {
		return usb_code;
	}

So if a module is getting SysEx over these messages, it would need to check usb_code if it was sure the sender was compliant with the USB MIDI standard, or check bytes[] for 0xF7 if the sender is not compliant with the USB MIDI spec (which I imagine might happen with DIN5->USB adapters)

I have a similar structure setup for native module MIDI (not using the rack layer), but I haven’t opened any API functions for it yet.

For outgoing MIDI, I made a little bit more of an interface for sending SysEx. Example:

 midi::Output midiOutput;
 midi::Message msg;

 // Send 3 bytes of sysex payload:
 msg.startSysEx(0x01, 0x02);
 midiOutput.onMessage(msg); // sends [04] 0xF0 0x01 0x02
 msg.endSysEx(0x03);
 midiOutput.onMessage(msg); // sends [06] 0x03 0xF7 0x00

 // Send 8 bytes of sysex payload over USB cable 9
 msg.setUsbCable(9);
 msg.startSysEx(0x04, 0x05);
 midiOutput.onMessage(msg); // sends [94] 0xF0 0x04 0x05
 msg.continueSysEx(0x06, 0x07, 0x08);
 midiOutput.onMessage(msg); // sends [94] 0x06 0x07 0x08
 msg.continueSysEx(0x09, 0x0a, 0x0b);
 midiOutput.onMessage(msg); // sends [94] 0x09 0x0a 0x0b
 msg.endSysEx();
 midiOutput.onMessage(msg); // sends [95] 0xF7 0x00 0x00
	 					    // Where [p#] is the USB header (p=cable)

Here’s the dev firmware:
https://github.com/4ms/metamodule/releases/download/firmware-v2.2.0-usb-cablenum-02/metamodule-firmware-v2.2.0-usb-cablenum-02-firmware-assets.zip

I haven’t checked if any of my controllers can send/receive SysEx yet so I only have tested this by mocking up data going in and out.

2 Likes

I can report that the Breakout box by SOMA LABORATORY works fine with the MM. :slight_smile:

It features:
1 USB-C
1 DIN MIDI IN
1 DIN MIDI OUT
AND 11 more CV OUT :smiley:

CV interface:
PC/MAC first voice channel . . . . . 1
PC/MAC second voice channel. . . . 2
PC/MAC CC channel . . . . . . . . . 0

PC/MAC output CC:
1st lower socket . . . . . . . . .CC20
2nd lower socket . . . . . . . . . CC21
3d lower socket. . . . . . . . . .CC22
1st upper socket . . . . . . . . . CC23
2nd upper socket. . . . . . . . CC24
3d upper socket . . . . . . . . . CC25
Tilt . . . . . . . . . . . . . . . . CC26
CC range 0–127 maps to –5 to 5 volts
with zero volts at 64.

MPE mode
can be turned on by pressing the button,
the XYZ LED will light up in MPE mode.

1 Like

Behringer X-Control working great with Meta module.

It has a USB Mini port and it worked fine with the lead that comes with the X-Control Mini via an Amazon USB-C/USB-A converter, and with a USB-C to USB Mini cable.

No issues found at all.

1 Like