jamulussoftware / jamulus

Jamulus enables musicians to perform real-time jam sessions over the internet.
https://jamulus.io
Other
990 stars 222 forks source link

MIDI Controller #95

Closed nicolopozzato closed 3 years ago

nicolopozzato commented 4 years ago

I have read that from the 3.4.4 (2020-03-25) version there is MIDI support.

- added support for controlling the audio mixer faders with a MIDI controller (MacOS and Linux)

How can i map my MIDI Controller to the mixer fader, solo and mute button? Also there is a way to map the rever effect and the mute my input?

I don't have find anything about it. Only some thing about it in the source code...

For info i have a Korg NanoKontrol2

corrados commented 4 years ago

Only the faders are supported.

corrados commented 4 years ago

The MIDI control is just experimental. Not really an end-user feature right now. Right now it is implemented for the Behringer X-Touch, see https://github.com/corrados/jamulus/blob/master/src/soundbase.cpp#L289 There is some offset of 70 to support that device.

nicolopozzato commented 4 years ago

Ok, could some sort of midi lerning interface be inserted? In order to make all devices compatible.

I imagine a settings page where you select a fader and then physically move it, in this way going to check which channel moves on MIDI you map it to the first channel of the mixer. So on for the next ones.

gilgongo commented 4 years ago

@nicolopozzato Dunno if this is relevant, but there's something in the docs on enabling MIDI on this page (scroll down, GH wiki doesn't do anchors).

nicolopozzato commented 4 years ago

I'm working on trying to change the MIDI support... This is a test to begin to help this incredible software!

Now channel70 is fader0 in jamulus

I was thinking about a new midi mapping... 1-20 General Purpuse command example:

1 -> Mute Stream (button)
2 -> Reverb Slider

Then group of 3 midi channel for every Jamulus Channel, so:

21 -> Channel Fader 0
22 -> Mute Button 0
23 -> Solo Button 0
24 -> Channel Fader 1
25 -> Mute Button 0
. . . . . . .

In this way everyone can map the note nedded in their MIDI Controller... (my friend has a Korg NanoKontroller2)

Does anyone know a midi program for linux that simulate midi presses? I don't have a midi controller, and my friend has a mac and we can't compile in his pc because we get error after following the guide in the wiki.

If anyone have idea how to integrate the midi support also for windows would be very nice!

dcocharro commented 4 years ago

Would be awesome to have MIDI support to control faders, solos and mutes integrated with a user friendly interface .

I attached some mockups to show some interface and functionalities ideas:

Screen Shot 2020-04-19 at 15 59 29

https://wireframe.cc/1piNNC Screen Shot 2020-04-20 at 19 25 06

nicolopozzato commented 4 years ago

I need to find a way to be able to test my code without a real midi controller...

For the moment I'm trying to connect to midi the mute button, I find the code a little bit messy so I need time to be able to understand the big picture.

As a first step I was thinking about a static channel mapping, the in the future to update to a "MIDI Mapping" window

dcocharro commented 4 years ago

I'm not a experienced developer, unfortunately I cannot help you much. For the sake of future reference, maybe you already know this better than me, I stumbled on this some time ago: https://www.music.mcgill.ca/~gary/rtmidi/ https://github.com/thestk/rtmidi Thank you @nicolopozzato

tanjeff commented 4 years ago

I believe that this issue addresses three topics which are only loosely coupled:

  1. The association between physical controls and Jamulus mixer strips
  2. The synchronization of physical controls with the Jamulus controls
  3. Additional Improvements features like scaling the range of the CC messages and making more controls MIDI-capable (Mute, Solo, Reverb, ...)

Regarding 1: I am aware of the following basic ideas:

Regarding 2: I would definitely prefer a MIDI channel Jamulus --> Controller, so the motorfaders (or LED indicators, or whatever) can be controlled by Jamulus. However, for controllers which do not support this (are there any?), the suggestion from dcocharro is cool: Wait for MIDI CC to pickup the current value, then move the Jamulus fader.

mawess commented 4 years ago

I am rehearsing with a big band in Jamulus (17+ strips) We definitely need to be a way to map a CC numbers to the strip to a specific user. (using client name ) (some text config file is totally ok, no GUI needed). So I always will have ten1 Esa at the same position.

the things we want to set is: volume, stereo panning, mute, solo

the config file should also default values.

we could then create such a file and send to the entire band.

corrados commented 4 years ago

we could then create such a file and send to the entire band.

Wow, all in your band of 17 members have a MIDI control hardware available for usage with Jamulus?

andreassch commented 4 years ago

The MIDI control is just experimental. Not really an end-user feature right now. Right now it is implemented for the Behringer X-Touch, see https://github.com/corrados/jamulus/blob/master/src/soundbase.cpp#L289 There is some offset of 70 to support that device.

What about reading the value of the offset from the settings (with a default of 70)? This should be relatively quick to implement and would enable a user to change the number by editing the settings file, i.e. without recompiling.

HughePaul commented 3 years ago

This is something I'm very interested in too.

The Mute Myself being on a midi controller is probably the one I'd use the most: the ability to have a foot pedal controller toggle that on and off.

I like the idea of MVP being able to configure the midi setup in a config/ini file on startup. Although a learn midi controller idea, and then it being tied to a user, wherever they end up in the fader list with sorting etc, would be amazing.

corrados commented 3 years ago

The Mute Myself being on a midi controller is probably the one I'd use the most: the ability to have a foot pedal controller toggle that on and off.

There are some solutions out there for this request, see, e.g.: https://sourceforge.net/p/llcon/discussion/533517/thread/f3252db0f5

andreassch commented 3 years ago

I would like to implement saving the MIDI control offset in the settings as proposed in my comment from 29th June. The simplest way I see is as follows:

Please comment.

corrados commented 3 years ago

Hm, maybe a quick hack is even simpler? We already have iCtrlMIDIChannel. The allowed channels are from 0-16 as far as I know. Integer has 2^31 values. So a lot to play with ;-). So we could define: if --ctrlmidich is in range 0-16 we do the same as before. If it is > 16 we could apply some math which extracts the MIDI channel number plus the offset. Ideas?

storeilly commented 3 years ago

16 channels total 0..15 or 1..16 depending on storage, only 4 bits required.

On Sun 15 Nov 2020 at 21:20, Volker Fischer notifications@github.com wrote:

Hm, maybe a quick hack is even simpler? We already have iCtrlMIDIChannel. The allowed channels are from 0-16 as far as I know. Integer has 2^31 values. So a lot to play with ;-). So we could define: if --ctrlmidich is in range 0-16 we do the same as before. If it is > 16 we could apply some math which extracts the MIDI channel number plus the offset. Ideas?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/corrados/jamulus/issues/95#issuecomment-727638680, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABIJSQL6BTONKHH7Q4FKTXDSQBAYXANCNFSM4MLNSA7A .

andreassch commented 3 years ago

One could use the first byte of iCtrlMIDIChannel for the channel and the rest for the offset. But maybe it's more clean to use two separate variables. iCtrlMIDIChannel could be reduced to an unsigned 8bit variable if you want to save space.

EDIT: One could also replace iCtrlMIDIChannel with a MIDISettings structure which contains the variables iCtrlMIDIChannel and iCtrlMIDIOffset. This is more clean and more flexible for potential future enhancements. If the size of both variables is small, e.g. 8 bit or 16 bit, the overall size does not increase.

corrados commented 3 years ago

It's not about saving space. We are talking about a functionality which less than 0.5 % of the Jamulus users use. So I want to keep the code added to Jamulus as small as possible. Extending the existing --ctrlmidich makes it possible that only the CSoundBase class must be changed slightly and nothing else.

What range do you need for the offset?

corrados commented 3 years ago

A very simple definition would be: --ctrlmidich [offset 3 digits][channel 2 digits], so e.g. for MIDI channel 11 and an offset of 723 you would use: --ctrlmidich 72311 If the channel would be, e.g., 7, you would use: --ctrlmidich 72307 For compatibility: --ctrlmidich 7 would result in MIDI channel 7 and an offset of 70 (offset which is hard coded right now).

andreassch commented 3 years ago

My master keyboard needs an offset of 20. As far as I know, the number has a range between 0 and 127, i.e. 7 bits.

Do you mean that you don't want to replace iCtrlMIDIChannel with a structure? That would enable to extend the functionality e.g. to buttons on the (hardware) fader strip (with the functionality to mute channels) or knobs on the fader strip (with functionality to pan) in a second step. This is useful e.g. when using the Jamulus client on a Raspberry Pie.

corrados commented 3 years ago

Ok, we could change iCtrlMIDIChannel to a string instead of an integer. Then you could do something like this: --ctrlmidich [MIDI channel];[offset for level];[offset for mute];[offset for solo];[offset for pan] So you could configure everything with just one command line argument.

andreassch commented 3 years ago

That's a good idea. Then the constructor of CSoundBase can parse the string and set member variables for channel, level offset, etc., but the other classes remain agnostic of the structure.

corrados commented 3 years ago

Exactly, that was my intention to keep the implementation simple. We already have a similar parser for the server properties:

        // split the different parameter strings
        slServInfoSeparateParams = strServerInfo.split ( ";" );

[...]

    // parse the server info string according to definition:
    // [this server name];[this server city]; ...
    //    [this server country as QLocale ID]; ...
    // per definition, we expect at least three parameters
    if ( iServInfoNumSplitItems >= 3 )
    {
        // [this server name]
        ThisServerListEntry.strName = slServInfoSeparateParams[0].left ( MAX_LEN_SERVER_NAME );

        // [this server city]
        ThisServerListEntry.strCity = slServInfoSeparateParams[1].left ( MAX_LEN_SERVER_CITY );

        // [this server country as QLocale ID]
        const int iCountry = slServInfoSeparateParams[2].toInt();

        if ( !slServInfoSeparateParams[2].isEmpty() && ( iCountry >= 0 ) && ( iCountry <= QLocale::LastCountry ) )
        {
            ThisServerListEntry.eCountry = static_cast<QLocale::Country> ( iCountry );
        }
    }
corrados commented 3 years ago

I have just setup a branch where I did some initial implementation: https://github.com/corrados/jamulus/compare/feature_MIDI_parameters

I cannot test the new code. Are you able to compile and test the code?

andreassch commented 3 years ago

Thank you very much for the implementation. I found a small bug in soundbase.cpp:236 (CSoundBase::ParseCommandLineArgument): due to the else the second part is never parsed (since count >= 2 implies count >= 1). Fixing that, it works fine with my hardware. Using a colon as separator is a little bit cumbersome because it has to be escaped from the shell. Maybe a separator that doesn't have to be escaped is handier.

corrados commented 3 years ago

due to the else the second part is never parsed

Thanks for testing. I just fixed this problem on the branch.

Maybe a separator that doesn't have to be escaped is handier

As we have a ";" as separator for --listfilter and --serverinfo, I want to keep it consistent for --ctrlmidich

andreassch commented 3 years ago

I understand that you want to keep it consistent.

To be able to listen to multiple input types (faders, buttons, knobs), the length of the fader strip has to be known, because all inputs send on the same channel. I would use the second argument (after the channel and before the offset) for the fader strip length and set the default to 8, the length of both the Behringer X-Touch (excluding the master fader) and my master keyboard. All messages with CC number ranging from offset to offset + fader_strip_length - 1 would then be identified with a specific type (e.g. fader).

Can you please give me a hint which function I should trigger to mute/unmute and to set the pan level? I found the code not so easy to follow and couldn't spot the right function. Thank you.

dakhubgit commented 3 years ago

Ok, I have some interest in this as well, particularly so since I have a Korg nanoKONTROL2 controller that is pretty much a spitting image of the usual mixer strips: DSC05264 rt It has a number of operating modes: an outdated "HUD" mode, a number of "Mackie control" interface modes where various controls are mapped to note on/off and other items, presumably to make for shorter MIDI messages than actual controller manipulations, and a native mode.

There is a simple controller definition program on the KORG homepage that can be made to work under Linux and Wine (counterintuitive to get its device selection to work under Wine). With that program you can reconfigure the used controls, whether the lights (all buttons except "Marker" and "Track" buttons can be lit) reflect the respective controller state ("internal") or follow what is being sent them ("external") and whether buttons are momentary (controller is 7F while button is pressed and becomes 0 again when released) or toggle, on a per-button basis. The default mode after reset, namely "internal" and "everything is momentary" does not make a whole lot of sense. Switching to "external" is achieved easily and is what should probably be considered the default way of operating since stuff can be changed in the Jamulus GUI, too.

My idea for a commandline interface would have been extending the --ctrlmidich spec from its current ch; fader0ctl spec to ch; fader0ctl; pan0ctl; s0ctl; m0ctl syntax. When preceding any of the controller specs with '+', Jamulus also provides a midi channel it writes to and it will write controller settings changed inside of Jamulus out to the controller. You'd likely want to write out values for fader/panner if you have motor faders and/or endless rotary controllers with position display (Behringer offers either). For the case of the nanoKONTROL2, you'd be writing just the S and M button values.

That does not yet give a good idea what to make of the transport controls (record button is obviously the inverse of the "Mute self" control, play button could be "connect", 'Track < >' buttons tend to be used for jumping the controller over 8 channel to the left/right (when more than 8 channels are actually available). But the main thing would be providing lit S/M controls. A note aside: Not sure what to do about R buttons. Make them read-only and indicate everyone who has not muted their channel? Use it to speak privately to one party (sort of the converse of the S button)?

At any rate: does that sound like a reasonable starting plan, or is it tomfoolery to overload the --ctrlmidich option in that manner?

dakhubgit commented 3 years ago

I need to find a way to be able to test my code without a real midi controller...

I can pitch in for the Korg nanoKONTROL2 here: it offers a "native" mode implemented with Midi controllers, and an application where you can define the controller numbers for every button/slider/pot and whether they act as one-shot buttons (events on both push and release) or as toggles (events on push). It can light most of the buttons via Midi commands, and it can also (by holding buttons while powering up) go into various non-native modes working mostly via note on/off events that include the Mackie control interface and HUD (some older interface used with a particular DAW). The application runs under WINE (it doesn't get the MIDI devices for both read and write right, you need to explicitly configure them in the settings menu, then manually select the nanoKONTROL2 as the device to configure, but then it works). Reasonably affordable second hand, runs via USB. Has no motor faders or "infinite" rotary controls, but at least feedback for the buttons.

So at a comparatively moderate price point, it's good for testing a whole lot of functionality of different more expensive controllers. And is quite convenient, actually.

dakhubgit commented 3 years ago

With regard to syntax: for more than one controller type it becomes important to figure out the count. I mulled through a number of schemes for figuring out the actual counts, but I think that this kind of second-guessing is likely not helping a lot.

So how about (for a nanoKontrol2)

-ctrlmidich 'v0-7;s32-39!;m48-55!;p16-23' Those are ranges for volume, solo (with writeback), mute (with writeback), pan. Alternatively -ctrlmidich 'v0*8;s32*8!;m48*8!;p16*8' Or maybe -ctrlmidich 'v0*8;s+32*8;m+48*8;p16*8'

Requiring ranges to be specified explicity allows avoiding to employ some magic for figuring out which function a particular controller belongs to.

andreassch commented 3 years ago

Since the length of the fader strip can be considered constant (i.e. the number of faders, pan knobs, mute buttons, etc. is the same), it is redundant to give it for each parameter. Thus, I suggest to give the parameters in the order channel, length of the fader strip, offset for faders, offset for pan, offset for mute, offset for solo. For your example for the nanoKontrol2 that would be

--ctrlmidich "0;8;32;48;16"

where 0 is the MIDI channel, 8 the length of the fader strip, 32 the offset for the faders, 48 the offset for pan, and 16 the offset for the mute buttons.

dakhubgit commented 3 years ago

andreassch notifications@github.com writes:

Since the length of the fader strip can be considered constant (i.e. the number of faders, pan knobs, mute buttons, etc. is the same), it is redundant to give it for each parameter. Thus, I suggest to give the parameters in the order channel, length of the fader strip, offset for faders, offset for pan, offset for mute, offset for solo. For your example for the nanoKontrol2 that would be

--ctrlmidich "0;8;32;48;16"

where 0 is the MIDI channel, 8 the length of the fader strip, 32 the offset for the faders, 48 the offset for pan, and 16 the offset for the mute buttons.

The scheme with a prefix letter and given count will also work for non-strip buttons like "mute transmission" (could be on the general "record" button). For generic controllers, the organisation might not always be in strips: one might use non-dedicated rotary controllers for pan. Also one can specify one but not the other, like if you don't use "mute" buttons.

That was sort of the rationale behind my proposal: doing something that does not use an artificial order one needs to remember and that we won't need to overhaul time and again.

-- David Kastrup

andreassch commented 3 years ago

I agree that your approach is more versatile. And letters as identifies make it easier to use.

ann0see commented 3 years ago

Since the PR was merged, I'll close this now. If it needs to be re opened, please re open it.