Go Down

Topic: Midi Time Code Reader/Generator with Arduino (Read 8409 times) previous topic - next topic

Grag38

Apr 05, 2011, 05:08 pm Last Edit: Apr 06, 2011, 11:31 am by Grag38 Reason: 1
This is a piece of code for Arduino to decode MidiTimeCode.

It can be usefull for some people that wants to deal with MTC.

Code: [Select]
/*


Midi Time Code Reader.

For information about MTC : http://home.roadrunner.com/~jgglatt/tech/mtc.htm


*/

#include <stdio.h>    // sprintf belongs to this lib

enum { F24 = 0, F25 = 2, F30DF = 4, F30 = 6 }; // Frames type

byte h, m, s, f;      // hour, minutes, secondes, frames time code
byte frameType;       // speed of MTC : 24fps / 25fps / 30 drop frame / 30 fps

byte buf[64];         // Serial Midi Buffer
byte tc[20];          // array to old timecode

void setup()
{
 Serial3.begin(31250);    // I use Arduino Mega TX3/RX3 serial / Modify for other arduino hardware
 Serial.begin(115200);    // To trace and display timeocde
}

void loop()
{
 int bytestoRead;

 bytestoRead = Serial3.available();

 if (bytestoRead>0)
 {
   for (int i=0; i<bytestoRead; i++)
   {
     buf[i]=Serial3.read();

     if (buf[i]!=0xF1)
     {
       int indice;

       indice = (buf[i] & 0xf0)>>4;

       if (indice>7)
         continue;

       tc[indice]= buf[i] & 0x0f;

       if (indice==7)
       {
         char toDisp[30];
         char typeStr[10];
         byte frameType;
         
         frameType = tc[7] & 0x06;

         h = (tc[7] & 0x01)*16 + tc[6];
         m = tc[5]*16+tc[4];
         s = tc[3]*16+tc[2];
         f = tc[1]*16+tc[0];

         if (h>23)  h = 23;
         if (m>59)  m = 59;
         if (s>59)  s = 59;
         if (f>30)  f = 30;

         switch(frameType)
         {
           case F24:
             strcpy(typeStr, "24 fps"); break;
           case F25:
             strcpy(typeStr, "25 fps"); break;
           case F30DF:
             strcpy(typeStr, "30df fps"); break;
           case F30:
             strcpy(typeStr, "30 fps"); break;  
           default:
             strcpy(typeStr, "Error"); break;
         }

         sprintf(toDisp, "TC %02d:%02d:%02d.%02d / %s", h, m, s, f, typeStr);
         Serial.println(toDisp);
       }
     }  

   }
 }
 
 delay(50);  // Save time for other things. Be carefull, every 2 frames as MTC works is around 8/100 of seconde !
 
}



Hope it can help few of you.

Grag

Grag38

#1
Apr 06, 2011, 11:20 am Last Edit: Apr 06, 2011, 06:58 pm by Grag38 Reason: 1
And now, MTC generator.

I used a timing library : http://www.arduino.cc/playground/Code/TimedAction

Easy code to modify, or adapt to other case. Accuracy is'nt good (timing stability along hours, must be implemented in other way with interrupts or else...)

Code: [Select]


#include <PCD8544.h>
#include <stdio.h>

#include <TimedAction.h>

volatile byte h, m, s, f;

//this initializes a TimedAction class that will send MTC quarter frame every 10 milliseconds so 80 milliseconde for
// an 'entire' timecode.

TimedAction timedAction = TimedAction(10,GenMtc);

//pin / state variables
const byte ledPin = 13;
boolean ledState = false;
static PCD8544 lcd;     // 5110 Nokia Lcd
char toDisp[14*6];      // 6 lines of 14 characters.


void setup()
{
 lcd.begin(84,48);
 pinMode(ledPin,OUTPUT);
 digitalWrite(ledPin,ledState);
 Serial3.begin(31250);               // use of TX3/RX3 of Arduino Mega
 h = m = s = f = 0;
}

void loop()
{
 timedAction.check();
}

void GenMtc()
{
 static byte indice=0;
 static byte toSend;

 switch(indice)
 {
 case 0:
   toSend = ( 0x00 + (f & 0xF));        
   break;

 case 1:
   toSend = ( 0x10 + ((f & 0xF0)/16));  
   break;

 case 2:
   toSend = ( 0x20 + (s & 0xF));        
   break;

 case 3:
   toSend = ( 0x30 + ((s & 0xF0)/16));  
   break;

 case 4:
   toSend = ( 0x40 + (m & 0xF));        
   break;

 case 5:
   toSend = ( 0x50 + ((m & 0xF0)/16));  
   break;

 case 6:
   toSend = ( 0x60 + (h & 0xF));        
   break;

 case 7:
   toSend = ( 0x72 + ((h & 0xF0)/16));   // 0x70 = 24 fps // 0x72 = 25 fps // 0x74 = 30df fps // 0x76 = 30 fps
   break;
 }
 
 Serial3.write(0xF1);
 Serial3.write(toSend);
 
 if (++indice>7)
 {
   f+=2;
   if (f>24)    // I'm French, so from standard is 25 frames/second
   {
     s++;
     f-=25;
   }
   if (s>59)
   {
     m++;
     s-=60;
   }
   if (m>59)
   {
     h++;
     m-=60;
   }
   ledState = ledState ? false:true;
   digitalWrite(ledPin,ledState);
   sprintf(toDisp, "%02d:%02d:%02d.%02d", h, m, s, f);
   lcd.setCursor(0,0);
   lcd.print(toDisp);
 
   indice=0;
 }
}

JLightMedia

Thank you so much! It's a very useful sketch! I work with different kind of time code and any information helps me to explorer this area. I've uploaded  "Midi Time Code Reader" sketch. It's working very good! Although I have one question. Why only even frames are detected? Is it possible to detect every frame? I want to crate "MTC to LTC Arduino converter" and I need to convert every incoming MIDI frame.

rolfmeurer

#3
Sep 22, 2018, 09:54 pm Last Edit: Sep 22, 2018, 09:59 pm by rolfmeurer
About the MTC generator.
1st of all a good idea, but far from perfect.
there are 2 mager mistakes in this project.

1st the usage of timedAction is not a solution in this case.
2nd the way to dice the seconds is improper.

Before you bother longer, please let me explain.


TimedAction(10,GenMtc);

The GenMTC() will be called every '10ms'

if (++indice>7)

after if (++indice>7) we have '80ms' by 8 times calling the GenMTC()


f+=2;
if (f>24)     // I'm not French I'm German :-)  but the standard is also 25 frames/second
{
s++;
f-=25;
}

this above is a real bugg,
I don't mean the f+=2, this is O.K. and might be helpful for a smoother display reading
What I mean is the way you'r calculating the seconds.

In this case the seconds are a result of counting the 25 frames in 13 steps up.
So we have the 80ms now 13 times and this makes 1040 ms a second. And this is wrong.
But more worse if we're using another fps (frame rate)

doing the example with 30fps, then we have 16 f++ steps, this makes 1280ms a second.

Yuk!

And here comes the solution:

1. We don't need the TimedAction.
but before we start the Generator, we must store the microseconds into a variable:
unsigned long timer = micros();

2. We must calculate the microseconds per Frame, those of course are different depending on fps
24fps means that each frame duration = 1000000*(1/24) microseconds
25fps means that each frame duration = 1000000*(1/25) ...
and exactly this is the time we have to wate beforefore we increase the f variable.
We need some variable casting to do this jop as perfect as possible by using:
unsingned long for the microseconds per frame variable
and float to casting the fps value
in other words:   
unsigned long microsPerFrame = (1 / (float)fps) * 1000000;  // (fps 24,25,29 or 30)
and the result is the time we have to wait, before we increment f
This is because (microsPerFrame * f == one second)

And here comes the way, we wait before we increase the f variable:

while (micros() < (timer + microsPerFrame));  // wait 1/fps seconds  or 1000000 * (1/(float)fps) micros()
timer = micros();                             // reset the timer variable for the next (f increasing) cycle
f += 1;                                       // and now, after 1/fps seconds, increase f

I hope this little turtorial will help you to get a responsable MTC Generator

cheers Rolf Meurer (the KRAFTWERK electronic engineer)

MarkT

This is a piece of code for Arduino to decode MidiTimeCode.


Code: [Select]

  ...
  delay(50);  // Save time for other things. Be carefull, every 2 frames as MTC works is around 8/100 of seconde
  ...


No, calling delay() will prevent other things running and may lose MIDI packets too.  Don't use delay()
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up