osune / MidiJoystick

MidiJoystick is a MIDI client for the Jack Audio Connection Kit on Linux, which lets you use your joystick to emit MIDI commands
MIT License
23 stars 5 forks source link

On/Off CC Messages (e.g. 0x40) don't work with buttons #7

Closed CharSnipeur closed 7 years ago

CharSnipeur commented 7 years ago

Hi, I'm interesting by your projet. I'm newbie on GitHub, I cannot find how contact you but by this issue :-(

I really want to use button to activate some effects in guitarix. I never learn Scheme, so I try some test and I can detect Release button but cannot catch them in your function. In fact, button send 64 (0x40) to MIDI, no 127 (0x7F) like you said in your README.

I deleted all, and starting with a C program (Haaaaaa, so easy). Some hours after testing, I have a little program. This program is useful for buttons and axes. He is very ugly, specially about configuration. If you are interresting by, I can send you my C program and .scm modify to print release event.

Bye

osune commented 7 years ago

Hey there, no problem.

To use the program you shouldn't know how to program in Scheme. To configure your Buttons all you need to modify is the 'input.conf' file.

I have problems to follow your question to be honest, so let me try to paraphrase what your question is and correct me if I am wrong:

You want to send a on/off Midi Message to guitarix with a button press. But the button just sends 0x40 instead of the documented behavior:

"Buttons emit midi values of 0x7F when pressed and no signal when released".

Is that right?

Please note that only Note-Off/ON, CC, Patch Change, Channel Pressure, Pitch Bend and SysEx Messages are supported. Depending on what kind of these message types you want to send, your button will work differently:

Message Type does button press use a value comment
Note-Off no joystick values are not used ( see readme)
Note-On yes with a button you can only play the Midi Note 0x7F
CC yes buttons send only the value 0x7F
PatchChange no the patch to change to is defined in the configuration
Pitch Bend yes but the bend value will be 0x7F
SysEx no SysEx is a fixed message with no parameters

As you can see Buttons are not that useful for parameterized messages, as the parameter will always be 0x7F. Buttons are best used with SysEx and PatchChange Messages (and some CC messages, as denoted in the 'Midi Compendium' linked in the readme). To realize a On/Off functionality one has to bind two messages to a button. For example:

 ;; Midi Real-Time Universal SysEx play/stop cmd on Start button
((b 7)
  ((#xF0) #x7F #x7F #x06 #x02 #xF7) ;; 1st / 3rd / 5th / .. press emits play cmd
  ((#xF0) #x7F #x7F #x06 #x01 #xF7)) ;; 2nd / 4th / 6th/  ... press emits stop
 )

If you are using a Button to send a parameterized message and the value 0x40 is emmited instead 0x7f it is a bug in my code. Please attach your 'input conf' so i can see what's up.

I don't know why you wrote a C program, but hey if it works that's cool i guess :)

If your C program shows me what you want to achieve (what Midi Message you want to emit) I would be glad to have a look. In this way I might get a better understanding of your issue and could provide a working input.conf or can fix a bug.

osune commented 7 years ago

Hey,

I took another look at the default configuration. And i think I might found the core of your issue. I've used a XBOX 360 controller and the default configuration. When I pressed the 'x' button (which is not defined in the configuration) the program emits sometimes the message: 0: e0 00 40

This is a Pitch Bend message on MIDI channel 1 with the Value 0x40 (meaning no pitch bend, as 0x40 is the center). But in the configuration this message is bound to the Y axis of the right analog stick:

 ;; Right Analog Stick Y Axis as Pitch Bend on Channel 1
 ((a 4) ((#xE0 #x00)))

Naturally the question arises 'why does pressing a button which should do nothing results in emitting a Pitch Bend message which is bound to an axis? "

The answer is easy: there are no deadzones on the axes. Little vibrations which occur when pressing a button result in movements on the axes. Which leads to a false positive recognition of axis movement in Midijoystick. These analog sticks are quite sensitive.

You have two possibilities to get deadzones working: calibrate your joystick/gamepad. Or use the deadzone flag which Midijoystick provides. The deadzone in Midijoystick is rather clunky as it will just ignore values which lie inside the deadzone, meaning it will ignore inputs until it reaches the deadzone border and then jump to the value.

As stated in the Readme:

The joystick api maps axes values to a int16_t (positive and negative) range. While midi data bytes range from 0x00 to 0x7F. So we’re mapping the axis values to uint16_t and then to the midi data range (0x00 - 0x7F), thus the real axis value of 0x00 is a midi value of 0x40. A real axis value of 0x00 occures when the axis controler is at center position.

A int16_t has a range of [-32767,32767] a uint16_t has a range of [0, 65535]. The deadzone is applied to the int16_t range. Which means invoking Midijoystick like that:

./midijoystick -d 30000

Will result in a quite unresponsive analog stick which will suddenly jump up to quite a high value. You will have to try around and find the sweetspot.

To wrap this up here are your options:

I hope this was your problem and is solved now. If not or something is unclear, don't hesitate and reply with further questions.

CharSnipeur commented 7 years ago

HI, thank you for your response. Yes, my english is very poor. My problem is that I can't use button in guitarix for switch on/off effects. Maybe is a problem with configuration file, I'll try (again) your SysEx config example. First time I don't success to use with guitarix. Maybe a problem with guitarix and SysEx messages ?

I configure button with ((b 0) ((#xB0 #x04) #x40 )) and it emit 0x40.

With my C code, I can catch release event of button. I emit x7F on pressed and x00 on release. Maybe the spririt of this can help you to make button use in CC mode usefull.

#include "glue.c"  
#include "mapping.h"
#include "joystick.h"

```struct js_event input_event;
#include <stdio.h>
//void ___SCMOBJ_to_U8(int,int,int){}
struct midi_msg Construit_msg(struct js_event*);
int main()
{
    _setup_jack();
    int fjoy=open_joystick("/dev/input/js0") ;
    int ev;
    struct midi_msg midi;
//    for (int i=0;i<6000;i++)
    while (1)
    {
      ev=get_joystick_event(fjoy, &input_event);
      debug_print_joystick_event(&input_event);
      if (input_event.type>2)
    continue;
      midi=Construit_msg(&input_event);
      /*midi.msg[0]=183;
      midi.msg[1]=67;
      midi.msg[2]=input_event.value*127;
      midi.size=3;*/
      jack_ringbuffer_write_space(jrb);
      jack_ringbuffer_write(jrb, (void*) &midi, sizeof(midi));
    }
    printf("fin");
    return 0;
}
///////////////////////////////////////////////////////////////
long  max_ax=32766,   min_ax=-32767;
struct midi_msg Construit_msg(struct js_event* event)
{
  int num=event->number;
  struct midi_msg midi;
  uint8_t **TX;
  short * sTX;
  if (event->type ==1)
  {
    TX=BX;
    sTX=sBX;
  }
  else
  {
    TX=AX;
    sTX=sAX;
  }
  for (int i=0;i<sTX[num];++i)
  {
    midi.msg[i]=TX[num][i];
  }
  printf("Num %d, premier %d\n",num,TX[num][0]);
  if (event->type ==1)
    midi.msg[sTX[num]]=event->value*127;
  else
    {midi.msg[sTX[num]]=((event->value-min_ax)*127)/(max_ax-min_ax);
      printf("VV=%u %u %u %d\n",midi.msg[0],midi.msg[1],midi.msg[2],sTX[num]);}
  midi.size=sTX[num]+1;
  return midi;
}

And the Ugly config file ``` const uint8_t AX0[]={177,4}; const uint8_t AX1[]={178,7}; const uint8_t AX2[]={179,12}; const uint8_t AX[]={AX0,AX1,AX2}; const short sAX[]={sizeof(AX0),sizeof(AX1),sizeof(AX2)}; const uint8_t BX0[]={180,64}; const uint8_t BX1[]={181,65}; const uint8_t BX2[]={182,66}; const uint8_t BX3[]={183,67}; const uint8_t BX4[]={184,64}; const uint8_t BX5[]={185,0x50}; const uint8_t BX6[]={186,0x51}; const uint8_t BX7[]={187,0x52}; const uint8_t BX8[]={188,0x7B}; const uint8_t BX9[]={0xF0,0x7F,0x7F,0x06,0x02,0xF7}; const uint8_t BX[]={BX0,BX1,BX2,BX3,BX4,BX5,BX6,BX7,BX8,BX9}; const short sBX[]={sizeof(BX0),sizeof(BX1),sizeof(BX2),sizeof(BX3),sizeof(BX4),sizeof(BX5),sizeof(BX6),sizeof(BX7),sizeof(BX8),sizeof(BX9)};

osune commented 7 years ago

No problem , I'm not a native speaker either. ;)

I think we are getting there. With the configuration line: ((b 0) ((#xB0 #x04) #x40 )) you are binding button 0 [(b 0)] to a Damper/Sustain [#x40] CC [#xB0] Message on Channel 5 [#x04].

As mentioned Buttons are always emitting 0x7F on parameterized messages. Because... well a button is not 'continous' by nature [it has only two states].

But you are right, as I noted myself in the Compendium, there are CCs which are not continous. The Damper/Sustain message is one of these. The range [0x00, 0x3F] denotes an off signal , the range [0x40, 0x7F] denotes an on signal.

That the code cannot handle this is a bug/limitation.

As you can see I didn't work on this project for over quite a year now, so it might take some time for me to get back into it. But I'll fix it.

Thanks for reporting!

osune commented 7 years ago

Hey

please have a look at the current master and commit 859cb63 .

Your usecase should be able now with following configuration line:

((b 0)
 ((#xB0 #x40) #x40 #x7F)   ; on first press activate Damper / Sustain
 ((#xB0 #x40) #x40 #x00)  ; on second press deactivate Damper / Sustain
)

With this syntax enhancement you can now supply a custom button value which is used with parameterized messages. This means it works with CCs, Pitchbend and Note-On/Off messages. But you still have to supply two lines to toggle between two states.

The new syntax looks like this:

((t i)
  ((CMD CH) PARAM BUTTON_VALUE))

Where BUTTON_VALUE is an optional value you want to send as parameter instead of the hardcoded 0x7F (which is btw now also fixed, you shouldn't get 0x40 anymore if you don't define BUTTON_VALUE in the configuration.

For example Note-On / Off would look like this:

((b 0)
 (#x80 #x00) #x40)    ; send note-off for midi note 64
 (#x90 #x00) #x40))  ;  send note-on for midi note 64

As BUTTON_VALUE is optional this means you can write something like this:

((b 0)
 ((#xB0 #x40) #x40)   ; on first press activate Damper / Sustain with _implicit_ 0x7F
 ((#xB0 #x40) #x40 #x00)  ; on second press deactivate Damper / Sustain
)

To save four characters to write ...

You can get crazy and use this this as a wonky stepped sequencer:

((b 0)
 (#x90 #x00) #x40)   ;  send note-on for midi note 64
 (#x80 #x00) #x40)   ;  send note-off for midi note 64
 (#x90 #x00) #x43)   ;  send note-on for midi note 67
 (#x80 #x00) #x43))  ;  send note-off for midi note 67

If it's not fixed don't hesitate to reopen this issue. :)