jln-ho
jln-ho

Reputation: 136

efficiently interpret serial data

I'm working on a c++ project that reads MIDI-data from an external USB-device. The program is supposed to call certain functions depending on which fader/knob/button on the USB-device is shiftet/rotated/pressed (such as vol +- or mute/unmute channel).

The only way I came up with finding out which fader/knob/button was changed was using a pretty big switch statement that basically checks every incoming midi event.

looks sort of like this :

switch(MidiMessage.get2ndByte()){

    case 1 : cout << "Fader 1 Value : " << MidiMessage.get3rdByte() << endl;  
    case 2 : cout << "Fader 2 Value : " << MidiMessage.get3rdByte() << endl;  
    case 10 : cout << "Button 1 Value : << "MidiMessage.get3rdByte() << endl;  
    ...
    ...
    ...
}

Isn't there a more efficient/smart way to do this?

Upvotes: 0

Views: 245

Answers (2)

Hasturkun
Hasturkun

Reputation: 36412

Most compilers will compile a large switch like that into a jump table (or a table lookup for simple values), so I would suggest you keep the switch.

If the only difference between the cases is the prefix string, I would suggest doing something like this instead:

const char *msg; // or std::string if you prefer

switch(MidiMessage.get2ndByte()){

    case 1 : msg = "Fader 1 Value : "; break;
    case 2 : msg = "Fader 2 Value : "; break;
    case 10: msg = "Button 1 Value : "; break;
    default: msg = "?"; break;
}

cout << msg << MidiMessage.get3rdByte() << endl;

Upvotes: 0

celtschk
celtschk

Reputation: 19731

Since your switching is done on a byte (and thus just has 256 different values; I'm pretty sure MIDI files are based on 8-bit bytes), the best option is probably to use a simple array of function pointers:

typedef void (*MidiAction)(MidiMessageType& message);

action_fader_1(MidiMessageType& message)
{
  std::cout << "Fader 1 Value : " << message.get3rdByte() << std::endl;
}

action_fader_2(MidiMessageType& message)
{
  std::cout << "Fader 2 Value : " << message.get3rdByte() << std::endl;
}

...

MidiAction midi_actions[256] = {
   /*  0 */ action_whatever,
   /*  1 */ action_fader_1,
   /*  2 */ action_fader_2,
   ...
   /* 10 */ action_button_1,
   ...
};

...

// this goes where your switch statement was:
midi_actions[MidiAction.get2ndByte()](MidiAction);

This array just uses 1KB (32 bit platforms) or 2KB (64 bit platforms), gives guaranteed constant time lookup, has no hidden overhead, and possibly your compiler implements your big switch statement internally as lookup table anyway (so the only overhead you get is an extra function call).

Note that if there are invalid byte values, the array entry should point to an explicit error function (instead of a simple 0), so your program can handle the error gracefully.

Upvotes: 2

Related Questions