Go Back   Cockos Incorporated Forums > Projects > Deprecated REAPER issue tracker > Open Bug

All 3 midi knob relative modes are affected by a enormous bug Issue Tools
issueid=4576 01-11-2013 11:06 PM
Human being with feelings
All 3 midi knob relative modes are affected by a enormous bug
When they get the MIDI value and they calculate the inc/dec, they start from offset 0 and not offset 1, making "1 step movements" impossible!

THE BUG: Reaper starts from +0 increments instead of +1 !!!!

Any keyboard is good for the test but best one is Axiom as having configurable encoders mode (so, all 3 relative modes).
The test can be done with ANY velocity mode. Ofc with no velocity 1-3 you cant use the (absolutely inaccurate) workaround and proof of bug.

test Mode1:
Axiom encoders setup in mode 147 (means 1-64 increments, 127-65 decrements)
Reaper in Relative 1 mode (the matching one).

test Mode2:
Axiom encoders setup in mode 146 (means 65-127 increments, 63-0 decrements)
Reaper in Relative 2 mode (the matching one).

test Mode3:
Axiom encoders setup in mode 149 (means 1-63 increments, 65-127 decrements)
Reaper in Relative 3 mode (the matching one).

Example in Mode 2 Easier to understand:
Relative2 is correct mode here.
a) for increments by 1 knob sends value 65 (higher value if accelerated)
b) for decrements by 1 knob sends value 63 (lower value if accelerated)
c) correct formula for both increments and decrements is INCOMING_VALUE-64 so by sending 65 you get 65-64=+1 (inc by 1). If you send 63 you get 63-64=-1 (so decrement by 1)
IF you send accelerated data like 74 means 74-64=+10 increments.

THE BUG: Reaper does INCOMING_DATA-65 for increments (results 0!) and INCOMING_DATA-63 for decrements (results 0!) making the whole thing not working on 1-by-1 step increments.

FIX: make reaper start from offset 1 and not offset 0 for the increments!
This issue happens in all three Relative Modes (tested putting encoders in correct mode for that specific mode, obviously)
The formula is broked in all 3 modes.
Issue Details
Issue Type Open Bug
Project Deprecated REAPER issue tracker
Category MIDI recording and playback
Status Awaiting Feedback
Priority 5 - Medium
Affected Version 4.31
Fixed Version (none)
Users able to reproduce bug 4
Users unable to reproduce bug 0
Assigned Users (none)
Tags (none)

01-13-2013 07:58 AM
I assume that this is for general mapping of the CC to actions/parameter values/etc, right?

Here is the actual code used for translating by mode in relative modes:
int adjustRel(int reladj, int adjmode)
  if (adjmode==1) { if (reladj >= 0x40) reladj|=~0x3f; } // sign extend if 0x40 set
  else if (adjmode==2) { reladj-=0x40; } // offset by 0x40
  else if (adjmode==3) { if (reladj&0x40) reladj=-(reladj&0x3f); } // 0x40 is sign bit
  return reladj;
Any suggested edits?
01-13-2013 11:48 AM
Human being with feelings
the function is indeed correct.
Is like there is a massive smoothing/attenuation applied toward VSTs then, with messages in relative mode. (tested various vsts i have, like Harmor, JP6k, sidizer and many others)

EXAMPLE:(sidizer + midi_logger to see vel)
for a full virtual knob turn (virtually 0 to 127) it requires 13 messages with velocity 0x7d (it should require just two messages being it 63+63=126!)
Normal knobs with absolute mode instead have a direct impact and 1 turn = min to max correctly.
01-16-2013 06:34 AM
Originally Posted by Justin
Any suggested edits?
Justin, please have a look on this other report http://forum.cockos.com/project.php?issueid=4589
Although this one deals with OSC learned actions, I have a vague feeling both issues might be tied (?)

(what I can tell for sure, from an extension plugin POV at least, is that we do not always get callbacks, i.e. sometimes KbdSectionInfo::OnAction() is not called at all - which could explain this other bug here: the nice adjustRel(int,int) you posted is perharps simply not called at all, related relative CC events being just ignored/dropped (?))
04-08-2013 04:42 PM
So the real issue is that the sensitivity when being bound to FX parameters should be increased?
This petition for a change to Confirmed is currently pending
07-01-2013 10:38 PM
Human being with feelings
EDIT: Just please see this in-line image, coupled with my following post for a possible solution.

Here is an image of the part in the Axiom manual where it shows what values you can set the rotaries to. This whole image is specific to setting the rotary encoders exclusively.

07-02-2013 12:09 AM
Human being with feelings
Ok, just for clarification:

I use the term clicks, because the rotary encoders "click" when you turn them. How these are called "smooth rotary encoders" I really don't know, because they're not. They Click.

After playing around with Schwa/midi_examine, I've found the following interesting information which may help you finally find a solution to this... Someone... please... already....

147 is actually backwards messages; ccw turns give the highest values whereas cw rotations give the lowest.

With that being said, I've instead set an encoder to cc option 146, which produces the desired results; cw for increment, ccw for decrement.

Also, with the Axiom, the rotary encoders specifically, because they use this non-orthodox method of assigning controls and setup options to these encoders, I've found that the encoders all need to be programmed with one of these CC values in the 146-151 range, but externally they send other CC numbers. In my case, I forget how I had set them up, but they are secondarily CC coded in the range of 12-19 between all eight.

The bottom four are 12-15
The top four are 16-19

NOW... With that being said, Here is what I found in Schwa/midi_examine using the desired control of 146:

In this case, I'm using the top-left one, which is CC coded primary 147 secondary 16. This secondary is what shows up in "data1"

mpos gives erratic, inconsistent, non-linear, non-consecutive, seemingly random values, and they are never the same in 100 clicks from what I've seen. I keep turning in clockwise directions, and all I get is numbers that jump up and down by large margins. Usually anywhere between 0-600. Velocity/speed at which I turn it does not matter. Don't know what's up there, but...

data1 contains the actual cc value of the encoder, in this case, the top-left encoder is set to a cc of 16. This is the midi note my encoder is set to above and beyond setting the rotary encoder to the CC control of 146.

data2 gives the following value when turned painfully slow, like, one click every 5 seconds CLOCKWISE: 65

data2 gives the following value when turned painfully slow, like, one click every 5 seconds, COUNTER-CLOCKWISE: 63

msg1 stays at 0 the whole time, regardless of direction or speed at which it's turned.

msg23 gives 16144 and 16656, countercw and cw, respectively. This is when the CC of the rotary encoder is set to 146, just as a reminder. Worth noting here is a speedy turn ccw gives lower values, lowest being 12,000, and turning cw gives higher values, highest I saw was around 27,000. But we're talking rediculous fast turns like you would never in your right mind want to turn it that fast unless you were expecting no less than an extreme of 0 or 127 value as a result.

Maybe additionally worth noting is that when the encoder is set to CC mode 147 instead of 146, msg23 gives 32529 and 273, ccw and cw, respectively. And here the "data2" value is 127 and 1, ccw and cw, respectively. THUS BACKWARDS ENCODING.

Also worth noting is when the ridiculous "throwing" of the rotary encoder to one side or the other is replicated, the "data 2" values lowest I saw from CCW turns was like 40, whereas highest I saw from CW turns was like 100.

status, statusHi, and statusLo DO NOT CHANGE. In this case these were 176, 11, and 0, respectively. For all of my encoders. In fact, none of the other fields really give me any useful information or feedback that changes.

So here is what I've found in the coding.
midirecv, the method which observes the position of whatever knob, button, slider, or what have you, only monitors for changes:


This in turn only sends the result to method midisend:


BUT what I've found is, the key data point here that produces the desired result for us is handled exclusively by the value produced in data2. As a result, we aren't getting any desired result, because Reaper is looking in the WRONG PLACE. Or, more precisely, the Axiom 25, or the whole series for that matter, sends the messages to the wrong place and thus it is not picked up by reaper at all.

When set to a midi control of 146, our key data point, "data2", meanders on the brink of the value "64" -/+ 1 counterclockwise(-) and clockwise(+) respectively. And of course -/+ MORE depending on how fast you turn it.

In fact, the value "64" is never seen. One turn clockwise puts the value at 65, and then a turn counterclockwise puts the value at 63.

SO here is the solution I propose.

A checkbox in the midi learn window which will switch to observing data2 for the increment or decrement value as opposed to the group of (mpos, msg1, msg23). Coupled with observing the field mpos to see when it changes values, specifically to know when to increase by one more increment for every time that data point changes. Of course this is not a velocity indicator, so keep that in mind. It's just some random number every time.

Or more precisely, we don't get any reliable feedback as to velocity of the rotary encoder, because there exists no solid indicator of velocity change other than flakey msg23 or even worse, our random mpos. Both of which would require constant calculation of the change between values; hence nothing really suitable for realtime velocity modification, or I could be wrong maybe one of those two or both would be good, and that's exactly perfect for it. You tell me.

So because two consecutive clicks in the same direction still produce the number either 63 or 65, (unless of course a ridiculous amount of "throw" in one direction is applied), you could know to increment by one more step on that second click by observing whether mpos DOES NOT EQUAL whatever the last value provided was. If the value changes, it increments 1 in the given direction.

Also, I'd like to mention that a global modification to all this for the Axiom specific would effect all other controls on the Axiom, and that is why I propose a checkbox specifically in the "Midi learn" window so it can be on an "as-per" basis. Because these changes would only apply for the encoders.

HERE is a few example "clicks" in one direction of the following values so you can get a better idea of what I'm talking about. Please keep in mind the fact that mpos is just random numbers up and down from who knows what it's observing, and msg23 is a velocity measurement (or at least the closest thing we have here). Also note I'm doing this in 10-second-plus increments; long enough to type them out. And I'm going clockwise:

Data2, MPOS, MSG23

65, 478, 16656
65, 211, 16656
65, 359, 16656
65, 166, 16656
65, 121, 16656
65, 511, 16656
65, 23, 16656
65, 457, 16656
65, 311, 16656

Ok, now here's some with fast "throws" or quick spins in the clockwise direction:

74, 55, 18960
92, 116, 23568
70, 119, 17936
127, 72, 32529
100, 70, 25616
81, 311, 20752
65, 217, 16656
76, 118, 19472
65, 166, 16656
84, 487, 21520

Here is the code for Schwa/midi_examine:

desc:Examine midi messages (http://www.midi.org/about-midi/table1.shtml)

slider1:0<0,255,1>sample offset within @block
slider2:0<0,255,1>status byte
slider3:0<0,127,1>data byte 1 (often note number)
slider4:0<0,127,1>data byte 2 (often velocity)
slider5:0<0,16,1>status high bits
slider6:0<0,16,1>status low bits (often channel)
slider7:0<0,8,1{-,note off,note on,poly aftertouch,control change,program change,channel aftertouch,pitch wheel,system special}>status high bits interpretation

// Data byte high bit is used for system exclusive messages, 
// we're ignoring it here.


  while (
    midirecv(mpos, msg1, msg23) ? (
      midisend(mpos, msg1, msg23);
      status = msg1;

      statusHi = (msg1 / 16) | 0;
      statusLo = msg1 - (statusHi * 16);     

      data2 = (msg23 / 256) | 0;
      data1 = msg23 - (data2 * 256);

      You could reassemble the message like this.
      msg1 = (statusHi * 16 + statusLo) | 0;
      msg23 = (data2 * 256 + data1) | 0;

      slider1 = mpos;
      slider2 = status;
      slider3 = data1;
      slider4 = data2;
      slider5 = statusHi;
      slider6 = statusLo;
      slider7 = statusHi - 7;

      sliderchange(255);  // We changed all the sliders.
Here is also a screenshot of my output window, which probably is of no help now that I've broke it all down for you, but here it is regardless. Note the data1 field as the culprit:

One click left / one click right

Now also additionally worth noting maybe, to put this into perspective with a control that actually works in Reaper, I provide the values of the Pitch wheel, for your viewing pleasure:

Full UP / resting MIDDLE / Full DOWN

Note how in the case of the pitch wheel, it at least, unlike the rotary encoders, contains the MIDI cc value message in it's "data1" field.

Well at any rate, I don't know what you guys would like to do about this, but I hope this has been a very informative post which helps you get down to the problem and get us Axiom users a solution for our rotary encoders; the final untouchable vestige of control on our otherwise halfway decent controllers, inside our otherwise DOPESAUCE * infinity DAW!!!!
07-12-2013 07:50 AM
Super Moderator (no feelings)
Please use the forum for general Q&A, this is the issue tracker (not a discussion forum) and everything not adding information to the bug itself should be posted in the forum. Thank you!
This petition for a change to Confirmed is currently pending
02-26-2015 10:26 AM
Human being with feelings
Originally Posted by Justin
So the real issue is that the sensitivity when being bound to FX parameters should be increased?
Yes, or scaled properly so that we can do 1 step movements.
1st issue: Using Endless rotary knobs, "1 step movements" is impossible! (mostly impossible.)
A knob or slider with 127 steps, when mapped to in relative mode is divided by 3 internally (somewhere after that code snippet pasted above, so an 0x03 midi byte tick moves it by 1)...... it needs a 384 step resolution slider to move 1 tick with an 0x01 byte.
I have independently found this issue, and a friend has independently confirmed it.

Additional ton of info before i summed it up into the 1 line above

A second issue has arisen due to tracking the first one down.
2nd / root cause / Real issue: A slider with a 128 step range is not large enough resolution for Relative Modes to move the slider by +1/-1 on a single tick forward/backwards of the knob. The necessary range is 383 steps (0-382) which smells of 384(why,idk). This is why it required a 0x03 MIDI data tick to move the 0-127 fader up one step. Sliders are variable resolution and use different sensitivity settings...

Here is a GIF animation 1920x1080 detailing the various slider behavior.

Each of the colored clips is labeled 0x01 to 0x05. This is the MIDI byte that it sends to the slider (using MIDItoReaControlPath for the demo but normal MIDI is identical.) Slider A1 and A2 are there for reference because they are highest resolution sliders I could find. The bottom slider, (H2) is the lowest resolution slider that responds to 0x01 MIDI CC commands. Each slider includes it's brother (ie H1) that is 1 value less, does not respond, and proves the (H2) is the lowest possible. Then it resets(Black clips are identical (0x40). This process repeats until you can see that not only are there cutoff values, but that the ratio does not remain constant,and there is overlap. For example faders that used to move together, with another status byte, now Dont.

The main issue is to show that 0-127 value faders don't respond until the 0x03 midi byte.

My knobs differ from the OP in the fact that they are Relative Mode 1 knobs. Mode 1 Means:
-1 tick (Left/CCW) Outputs 127 (0x7f)
+1 tick (Right/CW) Output 1 (0x01)
These endless rotary encoders support acceleration, by using 127 to 64 for left, or 1 to 63 for right. When you turn one right it starts at 1 and depending on how fast you rotate it it may jump to 3, 8 or whatever (max 63 or 0x3f). Left, same thing but starts at 127 to a min of 64 (0x40) Confirmed by MIDI logger.

The bug is as shown:
Wait for the start of the GIF - a blank black screen.
The knob CC is reaper-learned (as relative mode 1) to the slider at the very bottom...

Turning very very slowly and the MIDI data ticking +1 (or -1) does not actually move the fader at all. MIDI data of +2 also does not move it. +3 is the first value to actually move the fader (by 1). The problem is that by the time you got to 3 acceleration, you're already going quite fast and it tends to emit multiple ticks of 3, thereby moving the mapped slider by multiple increments. Single step increments are very hard to achieve in practice and I was trying very hard to do so for this GIF.

MIDI Byte2 Hex / Dec  |||  Tick Increment 
 00,01,02    "         =       +0 
 03-08       "         =       +1 
 09-0e     09-14       =       +2 
 0f-14     15-20       =       +3 
 15-1a     21-26       =       +4 
 1b-20     27-32       =       +5 
 21-26     33-38       =       +6 
 27-2c     39-44       =       +7 
 2d-32     45-50       =       +8 
 33-38     51-56       =       +9
 39-3e     57-62       =       +10
 3f         63         =       +11
 40         64         =       -11
All tested experimentally. From here it reverses.
All are Groups of 6 hex values (except 00,01,02 and 3f,40).
Wrong way:
1) In relative mode 1, MIDI data of "0x01" should move by 1 tick. IT DOESN'T. Ever. This is even easier to troubleshoot than "relative mode 2" and I cannot diagnose that myself but the OP's assessment seems fairly accurate.
2) There doesnt need to be a +11/-11. My guess is that it was never supposed to be there. This is way too fast of an acceleration anyway and it's only 1 value instead of a group of 6, leading me to believe that in reaper's code, each half was shifted by 1 as the OP describes.

Right way:
1) MIDI CC 0-127 is 128 values. 0 serves no purpose in relative mode, and is never sent. That leaves 127 possible values. Each half (+/-) should be equal. So we would omit #64. That would make 1-63 and 127-65, making two equal groups of 63. (this is standard)
2) 10 Groups of 6 = 60. Leaving 3 values un-accounted for.
How to proceed can be up for debate, but I have an idea.
3) MIDI data of 01 should move upward by 1 tick. Likewise for reverse, 0x7f hex (127 dec) should move down by -1.
4) MIDI data of 02 and 7e could be a +1/-1 tick also. Thereby making the first group have

Workaround involving 3rd party programs.
For reference, I have created a BOME MIDI Translator Pro rule that works perfectly, including acceleration and here it is:

Preset 0: Relative Knob with Acceleration to Absolute MIDI (on CC#06)
Translator 1: 1st rotary encoder
Incoming: MIDI B0 06 pp
if pp>=64 then pp=-128+pp
if ga>127 then ga=127
if ga<0 then ga=0
Outgoing: MIDI B0 06 ga

pp is Input CC Data. ga is the global variable accumulating the incremental ticks, which is then outputted.
The first line in rules does the equivalent of bitwise OR'ing the inverse.
The second line performs the addition of the incremental ticks (or negative ticks to go backwards).
And the 3rd and 4th lines cap off the global variable at 0 / 127 to support MIDI.

Questions: v5.0pre9 - January 28 2015 + MIDI learn: fixed action binding corner-case in relatve modes 2 and 3
Was this fixed? Is this unrelated?

Issue Tools
Subscribe to this issue

All times are GMT -7. The time now is 08:35 AM.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2022, vBulletin Solutions Inc.