Midi Time Code to SMPTE conversion (C++ / openframeworks)
I've recently needed to work with Midi Time Code (MTC) and could not find any code to parse the midi messages and construct an SMPTE timecode. Closest I got was finding this documentation (which is pretty good) on how the data is encoded in the bits of 8 bytes sent over 2 SMPTE frames, each byte sent at quarter frame intervals. From that I wrote the code below (I've only really tested the 25 fps). The code is from an openframeworks application but should work with any C/C++ code.
P.S. Some info on bits, bytes and nibbles here.
class ofxMidiEventArgs: public ofEventArgs{ public: int port; int channel; int status; int byteOne; int byteTwo; double timestamp; }; #define kMTCFrames 0 #define kMTCSeconds 1 #define kMTCMinutes 2 #define kMTCHours 3 // callback for when a midi message is received void newMidiMessage(ofxMidiEventArgs& eventArgs){ if(eventArgs.status == 240) { // if this is a MTC message... // these static variables could be globals, or class properties etc. static int times[4] = {0, 0, 0, 0}; // this static buffer will hold our 4 time componens (frames, seconds, minutes, hours) static char *szType = ""; // SMPTE type as string (24fps, 25fps, 30fps drop-frame, 30fps) static int numFrames = 100; // number of frames per second (start off with arbitrary high number until we receive it) int messageIndex = eventArgs.byteOne >> 4; // the high nibble: which quarter message is this (0...7). int value = eventArgs.byteOne & 0x0F; // the low nibble: value int timeIndex = messageIndex>>1; // which time component (frames, seconds, minutes or hours) is this bool bNewFrame = messageIndex % 4 == 0; // the time encoded in the MTC is 1 frame behind by the time we have received a new frame, so adjust accordingly if(bNewFrame) { times[kMTCFrames]++; if(times[kMTCFrames] >= numFrames) { times[kMTCFrames] %= numFrames; times[kMTCSeconds]++; if(times[kMTCSeconds] >= 60) { times[kMTCSeconds] %= 60; times[kMTCMinutes]++; if(times[kMTCMinutes] >= 60) { times[kMTCMinutes] %= 60; times[kMTCHours]++; } } } printf("%i:%i:%i:%i | %s\n", times[3], times[2], times[1], times[0], szType); } if(messageIndex % 2 == 0) { // if this is lower nibble of time component times[timeIndex] = value; } else { // ... or higher nibble times[timeIndex] |= value<<4; } if(messageIndex == 7) { times[kMTCHours] &= 0x1F; // only use lower 5 bits for hours (higher bits indicate SMPTE type) int smpteType = value >> 1; switch(smpteType) { case 0: numFrames = 24; szType = "24 fps"; break; case 1: numFrames = 25; szType = "25 fps"; break; case 2: numFrames = 30; szType = "30 fps (drop-frame)"; break; case 3: numFrames = 30; szType = "30 fps"; break; default: numFrames = 100; szType = " **** unknown SMPTE type ****"; } } } }
Delicious
Digg
StumbleUpon





