danomatika / ofxMidi

(maintained) Midi addon for openFrameworks
Other
262 stars 72 forks source link

Implement MTC natively #51

Closed s-ol closed 6 years ago

s-ol commented 7 years ago

I think this is a very powerful and important MIDI feature and as low-level as some of the features already implemented.

References: http://www.electronics.dit.ie/staff/tscarff/Music_technology/midi/MTC.htm http://www.memo.tv/archive/midi_time_code_to_smpte_conversion_c_openframeworks

working on my own 'user space' implementation atm, might submit PR once i get the quarter-frame messages working.

danomatika commented 7 years ago

The biggest hangup before was the lack of a cross-platform realtime timer, but there is a nanosecond timer now available in C++11. If you handle this within the receive parsing, it will also need to be added to the iOS message handling as well.

s-ol commented 7 years ago

@danomatika I meant just parsing incoming MTC messages (or providing a utility class for that). As a MIDI slave there is no native timing required (maybe for interpolation/smoothing? I didn't need that to sync video anyway). Output could be harder, but at least personally I don't care for it anyway 😄

s-ol commented 7 years ago

Since MTC stretches data points over multiple messenges, it would have to go into input state, which sounds bloat-y to me. So I'd rather have a seperate class that just attaches a handler to an input and provides you with up-to-date members to pull timing info from.

danomatika commented 7 years ago

That's why it needs to be handled internally to some degree as messages can be received while other, slower messages may come at the same time. This is true for sysex messages as well. I would organize it to be either received via the large ofxMidiMessage or via a smaller, MTC only callback so you can still ignore the timing messages to the regular callback. Sending MTC will involve a realtime clock and a thread.

s-ol commented 7 years ago

Still don't see where the timing comes into play - here is a version of what I would propose with the bit wrangling stripped:

class ofxMidiMTCClock : public ofxMidiListener {
  public:
    // MTC clock is only ready after receiving the first 8 quarter-frame messages
    // or a full-frame message
    bool ready;
    bool playing;

    int hours;
    int minutes;
    int seconds;
    int frames;

    int framesPerSecond;

    ofxMidiMTCClock(ofxMidiIn& input) {
      input.addListener(this);
    }

    ~ofxMidiMTCClock() {
      // reference or pointer needs to be stored somewhere, idk
      input.removeListener(this);
     }

    void newMidiMessage(ofxMidiMessage& msg) {
      switch (msg.status) {
        case MIDI_START:
          hours = 0;
          minutes  = 0;
          ...
        case MIDI_CONTINUE:
          playing = true;
          break;
        case MIDI_STOP:
          playing = false;
          break;
        case MIDI_TIME_CODE: {
          ...
          // update members
          break;
        }
        case MIDI_SYSEX:
          ...
          // update members
          ready = true;
        default: break;
      }
    }

    float getSeconds();
    int getMillis();
};
danomatika commented 7 years ago

The timing comes into play because if MTC support is to be added, it should be for both receiving and sending. I'm not saying it's something you have to do, only saying it would be best to have full support for MTC.

danomatika commented 7 years ago

When I read a PR titled "Implement MTC natively" I think that means sending too :)

s-ol commented 7 years ago

ah, I actually missed a word in your comment above, gotcha.

borg commented 6 years ago

Just leaving this here as it might be relevant/useful https://github.com/borg/ofxMidiClock

danomatika commented 6 years ago

IMO this is best done with a separate message parser as suggested by @borg. I don't have the time to do anything beyond maintenance of this addon.

danomatika commented 6 years ago

I changed my mind and ported over working MIDI clock and timestamp parsers for another project. See the midiTimingExample.