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

implement NPRN protocl for controlling TGs with a MIDI controller #604

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

simonvpe
Copy link

@simonvpe simonvpe commented Jan 19, 2024

Hello!

First off, I want to thank you for creating this amazing project. I found out about it on Floyd Steinberg's YouTube channel just a few days ago, and I've already started using it to make music.

I found some things were missing for my specific use-case so I had a quick couple of hours hacking away to add NRPN support to be able to control the TGs using my Arturia KeyLab 61 Mk1 MIDI controller. So this functionality is 100% working, but its implementation is a proof of concept as it uses some abusive data access shortcuts and is undocumented apart from the description I provide below.

Non-Registered Parameter Number (NRPN) is part of the Musical Instrument Digital Interface (MIDI) specification for control of electronic musical instruments. NRPNs allow manufacturer-specific or instrument-specific MIDI controllers that are not part of the basic MIDI standard.

The following CC controllers are used:

  • CC99 Non-Registered Parameter Number MSB (NRPN)
    • 0 >= value <= 5 selects operator 1-6 parameters of the TG specified by the channel of the message
    • value == 6 selects voice parameters of the TG specified by the channel of the message
    • value > 6 is ignored
  • CC98 Non-Registered Parameter Number LSB (NRPN)
    • 0 >= value <= 22 selects what parameter to write to the TG specified by the channel of the message, for example DEXED_OP_EG_R1, or one of the added parameters like MIDI_NRPN_PROGRAM_CHANGE.
  • CC38 Data Entry (LSB)
    • sets the value of the operator/voice parameter selected with CC99 and CC98

This is how I use it on my KeyLab

  1. I configure one of the encoders on my KeyLab to send a CC99=0...6 value, which allows me to select either an individual operator or the entire voice.
  2. I configure one encoder with CC99=127, LSB=DEXED_OP_EG_R1, Controller=38. When I turn the knob the controller will send (in order) CC99=127, CC98=DEXED_OP_EG_R1, CC38=<value of encoder>.

As you can see, the fact that MSB CC99=127 is ignored by MiniDexed I can effectively preselect the parameter destination for every knob and slider on my controller, achieving knob per function for an entire TG. Switching channel on my controller allows me to control any of the 8 TG's.

I also added parameter scaling so each parameter is controlled by the full resolution of the 7 useful bits of the data field of the message, i.e. 0-127. The reason for this is that it's not possible to configure the range of the KeyLab's encoders when they are in NRPN mode.

Now on to the elephant in the room.

I need some guidance for how to properly implement this so it is in line with the general architecture of the code. Some thoughts I've had so far.

  1. I'm exposing the CDexedAdapter pointer from the CMiniDexed as public, this is obviously a hack. There are some functions in Dexed like Dexed::setFCcontroller that could be exposed in the CMiniDexed instead, but I'm not sure which one to use or how.

  2. I don't know if the parameter scaling is duplicated code, nor do I know if this is the right place to do the scaling.

  3. There should be some degree of configurability. For example

  • Configuring the CC's used in the minidexed.ini file. Some people have non standard conforming equipment and in those situations it is useful to be able to re-configure things.
  • Enabling/disabling the parameter scaling in the minidexed.ini file. What should be the default here?
  1. I use a Dexed instance on my laptop to show me what's going on while I tweak the parameters. To keep the UI up to date with the TG I'm editing I'm sending an entire sysex voice dump for every parameter change, this should probably be sent as a smaller parameter change sysex according to the DX7 manual. Where should this code be placed? Or does it even exist somewhere else so I can reuse it? Should it be configurable in the minidexed.ini file?

  2. How to deal with menu updates?

  3. The order of the operators is reversed, setting CC99=0 selects operator 6. This is an easy fix but should be done after fixing the architectural problems.

I don't know if this PR is what the project wants. For my use case it is essential to get rid of any menu diving, I hate menu diving. If this PR is not in line with what the project wants I would still be happy to get the architectural guidance so I can get a better understanding of the code to maintain my own fork of the project.

So please, see this PR as a work in progress created to invite both philosophical and technical discussions. Or, whether or not to support NRPN at all, and how it's implementation should look like.

@simonvpe simonvpe marked this pull request as ready for review January 19, 2024 10:08
@probonopd
Copy link
Owner

@simonvpe thanks for this PR. I have to admit that I am having a hard time to understand the advantage of using NPRN over "good old" MIDI Sysex (like the DX7 and friends are also using.

Would be useful of owners of other controllers, too. I have a Nektar Impact LX61+, for example.

Would you be able to point me to some video that explains the concept and shows it in practice? How can I test it without an Arturia KeyLab?

@diyelectromusic
Copy link
Collaborator

Hi there - sorry, not quite sure why I've not really had a look at this one. I think there is something quite elegant about using NRPN for control like this.

I guess, if it was me, I'd probably implement a nrpn handler in Minidexed.cpp itself to keep that information about of mididevice.cpp. That might help the encapsulation and so on. We'd just need some basic checks in Mididevice.cpp to make sure it is a valid message for MiniDexed and then pass it into pSynthesizer.

Menu updates when under MIDI control is a common problem - we have it with all of the PC/CC messages received for example. I don't know that there is an easy answer really.

Just to confirm - this is all for a single TG correct? And looks like only for the voice parameters, so there is a question of whether this could be used with other control messages or if we just stick to MIDI CCs for those... (I think probably the latter).

And yes, being able to configure the NRPN used would be useful I think, but at the very least I'd make this whole behaviour a single config option - something like MIDINRPNVoiceParameters=0|1 (default on?)

Let me know if you want to take this further or if you'd like me to pick it up.

(Aside - rumours that I'd really like to build one of these are completely unfounded.... )

Kevin

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.

3 participants