Help correctly interpreting a signed integer received over MIDI SysEx data

Hi. I’m having an issue decoding a signed integer that is transmitted over MIDI in a SysEx message. The results that I’m getting are that negative numbers transmitted are being decoded as positive numbers, as if they were being treated as unsigned integers (although I am casting “int16_t”). Positive numbers transmitted are decoded correctly. I need to use signed integers because I will need negative numbers and positive numbers higher than 127.

The MIDI data is Bar Marker information in Universal Real Time SysEx data. Here is a description of it:

The Bar Marker message indicates the start of a musical measure. It could also be used to setup and mark off bars of an introductory “count down”.

F0 7F cc 03 01 lb mb F7

cc is the SysEx channel (0 to 127).

lb mb is the desired bar number, with the LSB first (ie, Intel order). This is a signed 14-bit value (low 7 bits are in lb, right-justified, and bits 8 to 14 are in mb, right-justified). Zero and negative numbers up to -8,190 indicate count off measures. For example, a value of -1 (ie, lb mb = 7F 7F) means that there is a one measure introduction. A value of zero would indicate no count off. Positive values indicate measures of the piece. The first measure is bar 1 (ie, lb mb = 01 00). A maximum neg number (lb mb = 00 40) indicates “stopped play” condition. A maximum positive value (lb mb = 7E 3F) indicates running condition, but no idea about measure number. This would be used by a device wishing to mark the passage of measures without keeping track of the actual measure number.

I have simplified and condensed my program to the below code, where I’m simply encoding and decoding the ‘bar’ number in the same progam, and the problem still persists.

int16_t bar = -20;

char encode_lb( int16_t value ) {
  return (char)((uint16_t)value >> 0) & 0x7F;
}

char encode_mb( int16_t value ) {
  return (char)((uint16_t)value >> 7) & 0x7F;
}

int16_t decoder( char lb, char mb ) {
  uint16_t v_combined = (uint16_t(mb) << 7) | (uint16_t(lb) << 0);
  int16_t v_signed = (int16_t)v_combined;
  return v_signed;
}

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  char lb = encode_lb(bar);
  char mb = encode_mb(bar);
  int16_t bardec = decoder(lb, mb);
  Serial.print("bar: ");
  Serial.println(bar);
  Serial.print("lb: ");
  Serial.println(lb, HEX);
  Serial.print("mb: ");
  Serial.println(mb, HEX);
  Serial.print("bardec: ");
  Serial.println(bardec);
  bar++;
}

Here’s the output I get:

bar: -20
lb: 6C
mb: 7F
bardec: 16364

bar: -1
lb: 7F
mb: 7F
bardec: 16383

bar: 0
lb: 0
mb: 0
bardec: 0

bar: 1
lb: 1
mb: 0
bardec: 1

The code is running on a Pro Mini (ATmega 328).

Any help to get the negative integer inputed (bar) to output the same negative integer (bardec) would be appreciated. Surely I’m missing something, but I can’t see it. I’ve done a lot of searching about this and haven’t found anything that works or that I can apply to this particular problem.

“F0 7F cc 03 01 lb mb F7” is a 32 bit value, try to use “int32_t” or “long” instead of a 16 bit type.

EDIT: If you want to shift 8 bits in order to get a byte from a word, you should shift by 8 and not by 7. You should also use 0xFF instead of 0x7F to get all bits, including the sign, from the byte:

char encode_lb( int16_t value ) {
  return (char)((uint16_t)value >> 0) & 0xFF;
}

char encode_mb( int16_t value ) {
  return (char)((uint16_t)value >> 8) & 0xFF;
}

int16_t decoder( char lb, char mb ) {
  return (int16_t(mb) << 8) | lb;
}

What I'm focusing on is "lb mb" which is a signed 14-bit value. I believe I'm correctly padding (two 8-byte) it when combining and separating. I am parsing the MIDI data correctly in my full code (i.e. I'm receiving note on/off etc. correctly). I'm sharing just the code where my 'decoding' of the "lb mb" number is failing. I've tried all other values, including "int32_t" or "long" and I am still not getting negative numbers. Only when I cast "char" do I get a negative number, but the program breaks—as expected—when I reach bar 128 in my program.

I may have misunderstood something… So, “int16_t bar” is supposed to symbolize one signed 14 bit value which you want to split into two, signed 7 bit values, yes? If so, you will need to mind the sign which has to be shifted left one place in order to “fill” a 8 bit type:

int8_t shift_sign(int8_t value)
{
  return (value & 0b00111111) | ((value & 0b01000000) << 1);
}

int8_t get_lm(int16_t value)
{
  return shift_sign(value & 0x7F);
}

int8_t get_mb(int16_t value)
{
  return shift_sign((value >> 7) & 0x7F);
}

The problem you’re facing is the lack of sign extension.

You can make use of arithmetic bit shifts - that take care of the sign extension for you in hardware - by shifting the most significant byte all the way to the left, and then shifting it to the left to the correct position. The least significant byte doesn’t need any special attention.

[color=#00979c]constexpr[/color] [color=#00979c]void[/color] [color=#d35400]encode[/color][color=#000000]([/color][color=#00979c]int16_t[/color] [color=#000000]val[/color][color=#434f54],[/color] [color=#00979c]uint8_t[/color] [color=#434f54]*[/color][color=#000000]buff[/color][color=#000000])[/color] [color=#000000]{   [/color]
    [color=#000000]buff[/color][color=#000000][[/color][color=#000000]0[/color][color=#000000]][/color] [color=#434f54]=[/color] [color=#000000]val[/color] [color=#434f54]&[/color] [color=#000000]0x7F[/color][color=#000000];[/color]
    [color=#000000]buff[/color][color=#000000][[/color][color=#000000]1[/color][color=#000000]][/color] [color=#434f54]=[/color] [color=#000000]([/color][color=#000000]val[/color] [color=#434f54]>>[/color] [color=#000000]7[/color][color=#000000])[/color] [color=#434f54]&[/color] [color=#000000]0x7F[/color][color=#000000];[/color]
[color=#000000]}[/color]

[color=#00979c]constexpr[/color] [color=#00979c]int16_t[/color] [color=#d35400]decode[/color][color=#000000]([/color][color=#00979c]const[/color] [color=#00979c]uint8_t[/color] [color=#434f54]*[/color][color=#000000]buff[/color][color=#000000])[/color] [color=#000000]{[/color]
    [color=#5e6d03]return[/color] [color=#000000]([/color][color=#00979c]int16_t[/color][color=#000000])[/color] [color=#000000]([/color][color=#000000]buff[/color][color=#000000][[/color][color=#000000]1[/color][color=#000000]][/color] [color=#434f54]<<[/color] [color=#000000]9[/color][color=#000000])[/color] [color=#434f54]>>[/color] [color=#000000]2[/color] [color=#434f54]|[/color] [color=#000000]buff[/color][color=#000000][[/color][color=#000000]0[/color][color=#000000]][/color][color=#000000];[/color]
[color=#000000]}[/color]

[color=#00979c]void[/color] [color=#5e6d03]setup[/color][color=#000000]([/color][color=#000000])[/color] [color=#000000]{[/color]
    [b][color=#d35400]Serial[/color][/b][color=#434f54].[/color][color=#d35400]begin[/color][color=#000000]([/color][color=#000000]115200[/color][color=#000000])[/color][color=#000000];[/color]
    [color=#5e6d03]while[/color] [color=#000000]([/color][color=#434f54]![/color][b][color=#d35400]Serial[/color][/b][color=#000000])[/color][color=#000000];[/color]
    [color=#00979c]int16_t[/color] [color=#000000]val[/color] [color=#434f54]=[/color] [color=#434f54]-[/color][color=#000000]20[/color][color=#000000];[/color]
    [color=#00979c]uint8_t[/color] [color=#000000]buff[/color][color=#000000][[/color][color=#000000]2[/color][color=#000000]][/color][color=#000000];[/color]
    [color=#d35400]encode[/color][color=#000000]([/color][color=#000000]val[/color][color=#434f54],[/color] [color=#000000]buff[/color][color=#000000])[/color][color=#000000];[/color]
    [b][color=#d35400]Serial[/color][/b][color=#434f54].[/color][color=#d35400]println[/color][color=#000000]([/color][color=#d35400]decode[/color][color=#000000]([/color][color=#000000]buff[/color][color=#000000])[/color][color=#000000])[/color][color=#000000];[/color]
[color=#000000]}[/color]

[color=#00979c]void[/color] [color=#5e6d03]loop[/color][color=#000000]([/color][color=#000000])[/color] [color=#000000]{[/color][color=#000000]}[/color]

Pieter

Thank you, PieterP, that solves my problem and now my code fully works! Cheers!

Glad to hear!

Alternatively, you can use a bit field struct with implicit conversions to int16_t. In that case the compiler will sign-extend when necessary:

[color=#00979c]int16_t[/color] [color=#d35400]decode[/color][color=#000000]([/color][color=#00979c]const[/color] [color=#00979c]uint8_t[/color] [color=#434f54]*[/color][color=#000000]buff[/color][color=#000000])[/color] [color=#000000]{[/color]
    [color=#00979c]struct[/color] [color=#000000]int14_t[/color] [color=#000000]{[/color] 
        [color=#00979c]int16_t[/color] [color=#000000]x[/color] [color=#434f54]:[/color] [color=#000000]14[/color][color=#000000];[/color]
        [color=#000000]int14_t[/color][color=#000000]([/color][color=#00979c]int16_t[/color] [color=#000000]x[/color][color=#000000])[/color] [color=#434f54]:[/color] [color=#000000]x[/color][color=#000000]([/color][color=#000000]x[/color][color=#000000])[/color] [color=#000000]{[/color][color=#000000]}[/color] 
        [color=#00979c]operator[/color] [color=#00979c]int16_t[/color][color=#000000]([/color][color=#000000])[/color] [color=#00979c]const[/color] [color=#000000]{[/color] [color=#5e6d03]return[/color] [color=#000000]x[/color][color=#000000];[/color] [color=#000000]}   [/color]
    [color=#000000]}[/color][color=#000000];[/color]
    [color=#000000]int14_t[/color] [color=#000000]v[/color] [color=#434f54]=[/color] [color=#000000]buff[/color][color=#000000][[/color][color=#000000]1[/color][color=#000000]][/color] [color=#434f54]<<[/color] [color=#000000]7[/color][color=#000000];[/color]
    [color=#5e6d03]return[/color] [color=#000000]v[/color] [color=#434f54]|[/color] [color=#000000]buff[/color][color=#000000][[/color][color=#000000]0[/color][color=#000000]][/color][color=#000000];[/color]
[color=#000000]}[/color]

It compiles down to the exact same machine code as the previous decode function, so pick whatever you find most readable.