Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Osc support (2nd try) #514

Draft
wants to merge 20 commits into
base: develop
Choose a base branch
from
Draft

Conversation

piegamesde
Copy link

As of #513, I hacked together a prototype for OSC support. It compiles on my system, and it won't compile any of the modules I don't currently use (fixing them is not that much work, just simply change the header). It hasn't been tested yet.

  • Everything that is related to MIDI, but not unique to MIDI has been renamed to form a new "Event" API.
  • The old API is still available using header files (typedef for the win).
    • Internal code must use a new JackMidi.h that does the type renaming. I could have kept all the old header files so that all the remaining code still works, but I didn't like the idea of having a dozen of almost empty files in the code base.
  • The MIDI channel type has been complemented with an OSC one
  • I'm not sure about the MIDI-specific things. Especially the MIDI drivers, since I don't see any equivalent for not-hardware-driven protocols like OSC
  • Net support is not clear at all. I'd like to pass all event ports using the existing MIDI code since changing the headers would break things between JACK versions I can't simply make backwards-compatible.

Feel free to object any of the design decisions.

@falkTX
Copy link
Member

falkTX commented Oct 11, 2019

This is quite nice!
Thanks for all the work so far.

Curious, how are bundles going to be handled here?

Can you add an example tool that uses this renamed API for OSC?

@piegamesde
Copy link
Author

Curious, how are bundles going to be handled here?

I thought I'd keep it simple: JACK is only the transport server and does not parse the sent data at all. This is the current behavior with MIDI (granted a few exceptions) and I'm not sure if I should change this. Clients may or may not send message bundles. They will be treated as event messages as any other message by JACK. Clients should make sure their messages are not too large (maybe we should increase the default buffer size?). Since each OSC event has its own time stamp system, event order for bundles is not that important.

Can you add an example tool that uses this renamed API for OSC?

Should I put it into the example_clients folder or should I rather create a new repository for it?

I cannot test this locally due to lack of hardware
I don't know how useful it is yet, maybe I'll remove it later on (tell me if you don't like it)
@falkTX
Copy link
Member

falkTX commented Oct 11, 2019

I thought I'd keep it simple: JACK is only the transport server and does not parse the sent data at all.

That makes a lot of sense actually, I am quite okay with this.
It is different from what the previous jack_osc project does, but that is quite fair.

Should I put it into the example_clients folder or should I rather create a new repository for it?

In the examples folder, I think such a tool could be the go-to example way of doing things.
I imagine something simple that puts jack osc messages into a ring buffer, and then passes them off to the network via liblo. CLI arguments define the protocol to use (UDP vs TCP) and the port.
jack_midi_dump can serve as an example as it uses a ring buffer to print messages out of the RT process function.
what do you think?



/** A Jack MIDI event. */
typedef jack_event_t jack_midi_event_t;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be the other way 'round.
For ABI compatibility the library needs to expose the jack_midi_event_t symbol.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aw, crap. Are you sure there isn't a way to make this work? Do I have to redo all my renaming work or only the header files in common/jack?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes indeed. having the macros makes it API compatible, but not ABI.
the MIDI functions need to be actually defined. they will be just calling the event API, but they need to be there for visibility and backwards compatibility

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could write wrapper functions and export those.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implies that jack/eventport.h must include jack/midiport.h. So we are going to be stuck with the old headers always included until the next major version bump :(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the other way around. midiport.h needs to include eventport.h and define the types in a way that keeps backwards compatibility.
clients built against new jack need to work with older jack and vice-versa, the exception being if a client uses the new API not available on the older versions (obviously)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "vice-versa" part in particular. say you have an old, closed-source JACK app. You cannot re-compile it. That still needs to work with modern JACK.

That app might be bitwig, or might be baudline,..

JACK's ABI has been stable since it's first release and is not versioned (not even major versions break the binary interface).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may need help on this one, I'm lost in the C header hell with obscure compiler messages

@piegamesde
Copy link
Author

@falkTX I got a few ideas for useful OSC tools. I think I'm going to start with as simple MIDI->OSC->MIDI bridge and escalate as needed. Would you mind the example being in Rust?

@falkTX
Copy link
Member

falkTX commented Oct 11, 2019

Actually yes. rust does not build for all the targets that jack does, so using rust would be quite limiting.
Please stick to C or worst case C++.

@beatboxchad
Copy link

I'm sold. \m/ nice going, this kinda rules!

@piegamesde piegamesde mentioned this pull request Oct 11, 2019
@ventosus
Copy link

ventosus commented Oct 12, 2019

Hi

There currently are at least 2 projects already doing OSC via JACK, afaik.

  • zynaddsubfx (stable)
  • synthpod (unstable)

They just reuse the JACK MIDI ports and API unchanged. To discriminate OSC ports from JACK ports, they are tagged with metadata. The port metadata has been defined by @drobilla at a LAC some years back [1]:

"http://jackaudio.org/metadata/event-types" "OSC"

Some of those metadata keys seems to have been imported into JACK2 source, but not all, it seems @falkTX ?

This does not break anything and is backwards compatible to the beginning of JACK, because OSC and MIDI can coexist on the same event port:

First byte of OSC is either '/' or '#', both are no valid first MIDI byte. So a application handling only MIDI messages, will just ignore OSC and vice-versa.

So, a new event port is really not needed, imho and tagging ports with the correct metadata is super simple.

It also has the advantage that it is easier to extend. e.g. say I want to route LV2 atoms via JACK ports (I've actually done that), I only need to define a new metadata key and make sure that the first byte does not clash with either MIDI or OSC. Going the dedicated-event-port-per- event-type-route, this will always need a new JACK release and be backwards incompatible.

If there really should ever be the need for a new event port type, then please take inspiration from LV2 and create an event port type for all possible future event types and extend the event structure with a proper type, will make any further request for new port types superfluous:

typedef struct _jack_event_event
{
       	jack_nframes_t    time;   /**< Sample index at which event is valid */
    	size_t            size;   /**< Number of bytes of data in \a buffer */
            uint32_t        type;  🌹*<Type of this event */
    	jack_event_data_t *buffer; /**< Raw MIDI data */
} jack_event_event_t;

[1] https://github.com/drobilla/jackey/blob/master/jackey.h

@ventosus
Copy link

zynaddsubfx has a simple header with some macro definitions[1] and then just uses that [2] + tagging the port with the proper metadata [3].

[1] https://github.com/zynaddsubfx/zynaddsubfx/blob/master/src/Nio/jack_osc.h
[2] https://github.com/zynaddsubfx/zynaddsubfx/blob/master/src/Nio/JackEngine.cpp#L320
[3] https://github.com/zynaddsubfx/zynaddsubfx/blob/master/src/Nio/JackEngine.cpp#L249

@piegamesde
Copy link
Author

@ventosus This is cool. My goal is to be fully backwards compatible to existing solutions like these. I am probably going to remove the different port types soon. The only thing I don't like is having to be "MIDI-compatible" regarding the start byte. If the metadata things works properly, we can rely on our patchbays to warn from accidental port content mismatch.

@ventosus
Copy link

ventosus commented Oct 12, 2019

@ventosus If the metadata things works properly, we can rely on our patchbays to warn from accidental port content mismatch.

Now that JACK2 finally has metadata support, too, more projects could actually use this, yes.

Instead of warning, patchbays should just refuse to connect differing event ports (jack_connect could be adapted as an example). patchage [1] does already do that since years, iirc (when run on JACK1, because JACK2 is a late adopter of metadata :-). patchmatrix [2], too.

[1] http://drobilla.net/software/patchage
[2] https://open-music-kontrollers.ch/lad/patchmatrix/

@ventosus
Copy link

@piagemesde

If there's no new event structure, no a new port type, an additional header (e.g. jack/eventport.h) with some defines and typedefs like [1] would be enough, imho (e.g. just replace osc with event).

[1] https://github.com/zynaddsubfx/zynaddsubfx/blob/master/src/Nio/jack_osc.h

There are only two now, `gAudioPortType` and `gMessagePortType`. There is a string constant for `JACK_DEFAULT_MIDI_TYPE` which will be handled separately.
@ventosus
Copy link

last time I checked, JACK OSC messages were not routed via either netjack1, netjack2 or both ?

Needs some investigation, I guess...

- Added inline helper methods to create ports of the correct type
     - Had to move `jack_port_uuid` to the top because we still can't use methods declared below in 2019
    - I'd like to add helper methods that automatically set the metadata but C has no method overloading?!
- Improved metadata documentation
- Registering a port with the `JACK_DEFAULT_MIDI_TYPE` will automatically change it to `JACK_DEFAULT_MESSAGE_TYPE` for forward  compatibility.
@piegamesde
Copy link
Author

@ventosus If I just add a new header, this means that we will stick internally with processing "MIDI" even if in reality the buffers may contain all sorts of data. I fear that this will lead to confusion or even bugs.

Honestly, I have no clue how to update netjack while keeping backwards compatibility. We can rename the MIDI ports to message ports with ease, but a) I don't know how MIDI-specific the data handling is here and b) I don't know if netjack handles metadata yet.

@piegamesde
Copy link
Author

@x42 Please have a look at piegamesde@8614e5d. I redid that commit (with slightly different naming) from scratch to get a proper diff.

@piegamesde
Copy link
Author

@ventosus Do we really need JACK_METADATA_EVENT_TYPES to be able to contain multiple values? This is quite a pain to process, honestly, and I don't really see the use (you could still split up the port to one per protocol, or only tag the most prominent one).

@ventosus
Copy link

@ventosus Do we really need JACK_METADATA_EVENT_TYPES to be able to contain multiple values? This is quite a pain to process, honestly, and I don't really see the use (you could still split up the port to one per protocol, or only tag the most prominent one).

@piegamesde probably not (and the linked projects don't do that, either). @drobilla as the author of that key may remember the rationale for it, I don't remember, sorry.

@drobilla
Copy link

It's for multiplexing. Though it's slightly more annoying to deal with the metadata, this is nothing compared to having to deal with a bunch of separate event ports of different types, compared to a single one with a well-ordered sequence of events. Aside from being a massive hassle to deal with in a sample accurate context, many ports is also quite a bit slower.

This way you can also extend an app to support OSC (or whatever) without completely breaking its interface or making routing weird, because you can naively route around the "control events" without really having to care what they are.

Generally speaking, this stuff is trying to pretend that event ports are the way they should have been: a sequence of time stamped whatevers. I agree it's not pleasant, but c'est la vie.

@piegamesde
Copy link
Author

@drobilla So I'm going to write something to check port compatibility, hide the ugliness in helper methods. What are the semantics of an output port having multiple protocols? As I understand it, that port may emit events in any of those.

Say I have a port that may send OSC and MIDI data. Can I connect this to OSC ports or must these ports implement both OSC and MIDI as well? I'd think the latter one, because that's how you do type safety in programming languages. (Does this matter anyway? Are there even ports that can send both MIDI and OSC?)

@piegamesde
Copy link
Author

@drobilla Instead of allowing multiple values in this field, what about defining one value that has the meaning of "any protocol"? You'd still be able to do multiplex routing MIDI and OSC into one port, but without the pain of having to handle a list of elements.

@drobilla
Copy link

drobilla commented Oct 19, 2019

But then you can't tell what it supports (this is also roughly how it works in LV2). I get that it's mildly more annoying to use than simple one value properties, but.... oh well? I don't think the few lines of C that it takes to look for something in a list outweighs the actual usefulness.

It's a compromise because metadata keys can only have one value. Since this is the general way you deal with that, some utilities for it will probably come in handy anyway.

An output port that has both may send both.

I'll try to actually read this branch in detail tomorrow and hopefully get a better idea of how this ties in with the metadata stuff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants