5

I'm trying to make a sort of custom midi player, to do so I'm using an array that already has memorized correctly the midi messages data like this:

int array[3000][4]={{time,status,data1,data2},{...},...}

when I want my program to send the midi message (so that it can be played) I call this array and do the needed distinctions between noteon/off, pitch-bend and such. The pitch-bend value (ranging from 0 to 16383, but usually is around 8192 which means no pitch shifting) is all memorized in data1 (array[i][2]). For the conversion from int to the two 7 bits values to pass to midiOutShortMsg() I've used some of the code I found here. Here is the code I'm actually using:

union { unsigned long word; unsigned char data[4]; } message;
int main(int argc, char** argv) {
    int midiport; // select which MIDI output port to open
    uint16_t bend;
    int flag,u;    // monitor the status of returning functions
    uint16_t mask = 0x007F;
    HMIDIOUT device;    // MIDI device interface for sending MIDI output
    message.data[0] = 0x90;  
    message.data[1] = 60;    
    message.data[2] = 100;   
    message.data[3] = 0;     // Unused parameter


// Assign the MIDI output port number (from input or default to 0)
if (!midiOutGetNumDevs()){
    printf("non ci sono devices");
}
if (argc < 2) {
    midiport = 0;
}
else {
    midiport = 0;
}
printf("MIDI output port set to %d.\n", midiport);

// Open the MIDI output port
flag = midiOutOpen(&device, midiport, 0, 0, CALLBACK_NULL);
if (flag != MMSYSERR_NOERROR) {
    printf("Error opening MIDI Output.\n");
    return 1;
}i = 0;
message.data[0] = 0xC0;
message.data[1] = 25;
message.data[2] = 0;
flag = midiOutShortMsg(device, message.word); //program change to steel guitar
if (flag != MMSYSERR_NOERROR) {
    printf("Warning: MIDI Output is not open.\n");
}
while (1){
    if (array[i][1] == 1) { //note on 
        this_works();i++;
    }
    else if (array[i][1] == 0){//note off
        this_also_works();i++;
    }
    else if (array[i][1] == 2){//pitch bend
        while (array[i][1] == 2){
            Sleep(10);
            message.data[0] = 0xE0;
            bend = (uint16_t) array[i][2];
            message.data[1] = bend & mask;
            message.data[2] = (bend & (mask << 7)) >> 7;
            printf("bending %d, %d\n", message.data[1],message.data[2]); 
            flag = midiOutShortMsg(device, message.word);
            if (flag != MMSYSERR_NOERROR) {
                printf("Warning: MIDI Output is not open.\n");
            }i++;
        }
    }
}}

The printf("bending %d,%d") function always prints the first %d as a 0, no matter what. It's the first time I program in midi and I never had to deal with 7 bits values before, so I'm getting quite confused, any help will be appreciated.

Community
  • 1
  • 1
Febio Mosca
  • 127
  • 10
  • `bend = array[i][2]`, looking at the array initialization in position 2 there is a variable named `byte1`, so if you shift the mask to grab after the first seven bits `bend & (mask << 7)` you only get the last bit in byte1, which for midi bytes should always be zero. – ryanpattison May 19 '15 at 22:17
  • If the data is already separated into two bytes in the array, why do you need to separate it again? just put array[i][2] and array[i][3] into message.data[1] and message.data[2] – samgak May 20 '15 at 01:16
  • I think you might have mistaken the meaning of byte1 and byte2, I shall rename them data1 and data2. The data1 (ex byte1) variable contains an int number ranging from 0 to 16383 that I have to divide into 2 separate variables of 7 bit each (message.data[1] and message.data[2]). I'll edit my question. – Febio Mosca May 20 '15 at 01:53

1 Answers1

10

For a pitch bend message data1 (your message.data[1]) is the LSB, and data2 (message.data[2]) is MSB. I'm not a C developer, but here's how I do it in some pseudo-code:

(byte) data2 = pitchbend >> 7
(byte) data1 = pitchbend & 0x7F

In English:

  • MSB is: pitchbend bit shift right 7
  • LSB is: pitchbend bitwise AND with a mask of 127

For reference, doing the reverse (combining the two values to calculate pitch bend, if you had received them in a message, for instance) is a simple matter of:

pitchbend = (data2 * 128) + data1

Edit: I read your code more closely, and it looks like you're already doing what I described. IE:

uint16_t mask = 0x007F;
bend = (uint16_t) array[i][2];

message.data[1] = bend & mask;
message.data[2] = (bend & (mask << 7)) >> 7;

What values for array[i][2] are you trying to send? Anything that is an even multiple of 128 will result in an LSB (message.data[1]) of zero. It is not uncommon for devices to ignore, or not use the added resolution provided by the low byte, so your sample MIDI data may fall under this condition.

Justin Ryan
  • 1,409
  • 1
  • 12
  • 25
  • 1
    bitwise AND == & Logical AND == && –  May 20 '15 at 09:48
  • This is overly complicated as data2 is simply pitchbend >> 7; no additional masking is needed since the 2 MSB are zeroes. – Aki Suihkonen May 20 '15 at 09:55
  • @Justin Ryan I didn't notice that actually all the pitchbends value I have stored are multiples of 128. Yet I don't understand why the midi player doesn't play the actual pitchbend as it should. It sounds like it's bending less (much less) than a semitone while it should sound like a bend of a semitone with vibrato at the end. – Febio Mosca May 20 '15 at 11:18
  • @Miguel I'm not sure I understand your suggestion – Febio Mosca May 20 '15 at 11:19
  • @Aki I tried what you suggested and it's true, it still gives the same result (both numerically and midiwise). – Febio Mosca May 20 '15 at 11:19
  • @FebioMosca Both of their comments were directed to me, addressing nuances of my original answer which I have edited to incorporate. I'm glad that you've found the source of your unexpected results. – Justin Ryan May 20 '15 at 11:24
  • I would say that I found out why the numerical result I have shouldn't be unexpected, unfortunately I still don't know why the midi doesn't play as it should. I guess I'll have to search for a solution and eventually post another question. Thanks for the help. – Febio Mosca May 20 '15 at 11:54
  • Did you set the pitch bend range on the receiving device? Pitch bend values by themselves don't determine how many semitones of pitch bend you'll hear. i.e. sending a pitch bend value of 16383 to a device whose pitch bend range is set to a semitone will result in a bend of a semitone. Sending a pitch bend value of 16383 to a device whose pitch bend range is set to an octave will result in a bend of an octave. – SSteve May 20 '15 at 13:54