Audio Library for Linear Timecode (LTC)?

Ok, i'll test it this evening :)

Well... it works with the Teensy 3.2, default 72MHz.
There seems to be a little problem with the first bit:
Code:
16:19:55.00
16:19:56.00
16:19:56.02
16:19:56.02
16:19:56.04
16:19:56.04
(skips frame numbers). Perhaps a little timing problem maybe bit 0 is a little too long. Edit: Yes, pls change the "magic constant" from 1.0f to 0.5f.

But you should see something. Is the wire connected?
 
Last edited:
Yes, I did have the wire connected as you described in your post.

I think it's something with my development environment.
How should I set the USB in the arduino IDE?
I tried setting the USB to serial, audio, and audio+midi+serial.


-Tom
 
Best is to use the default, "serial".

If you add
Code:
  delay(1500);
  Serial.println("Hello Tom");
as last lines to setup() - what happens?
 
I set the USB mode back to the default "serial" and added the code at the end of setup, and once I reboot the teensy (after loading the program) I get "Hello Tom" on the serial port, but nothing else. So I recheck my wiring... and yep. I had it wired pin 0 to pin 16. Changed it back to pin 1, and now it works just as yours does.

Now on to testing...

I updated the magic speed adjustment from 1.0f to 0.5f, and now more of the frames are showing up, though something happens at the 24th frame: it shows 00, but then doesn't show 01. I wonder if the timing of switching to the next second is the problem?

20:00:47.22
20:00:47.23
20:00:47.00
20:00:48.00
20:00:48.02
20:00:48.03
20:00:48.04

So I tried changing the magic speed adjustment to 0.25 and then it started working better- all frames were showing up fine in the serial monitor.

Next I decided to try sending the generated LTC signal out to my laptop audio input. So I connected a pot across the output signal from pin 1 and GND on the teensy, then connected the pot wiper to the tip and GND to the sleeve of a 3.5mm audio plug. With the voltage divider circuit, I can set the input to a good level and not overdrive the PC audio card input.

I fed this signal into a demo of the LTCReader application. While it didn't work very reliably, it did for a few moments show the same timecode as the teensy decoder was showing on the serial port. I'm not sure if the problem is with our signal or with the LTCReader software being a demo.

Here is an image of the audio waveform that I'm receiving from the teensy:
test waveform.jpg

Finally, I tested the older version of your LTCdecode program by sending the recording I'd made back into the Teensy as a USB audio device. This time the code showed the following:
zero20:03:06.01
20:03:06.02
20:03:06.03
20:03:06.04
20:03:06.05
20:03:06.06
20:03:06.07
20:03:06.08
20:03:06.09
20:03:06.10
20:03:06.11
20:03:06.12
20:03:06.13
20:03:06.14
20:03:06.15
20:03:06.16
20:03:06.17
20:03:06.18
20:03:06.19
20:03:06.20
20:03:06.21
20:03:06.22
20:03:06.23
20:03:06.00
zero20:03:07.01

Which looks fine to me (the 'zero' was in the code for testing I assume).

I'll need to run some more tests to try to figure out if we are indeed generating a good LTC signal or not.

-Tom
 
Last edited:
Thank you for testing :)

I noticed a few glitches on the output with slow f_fcpu. Looks like there is a higher priority interrupt somewhere or the Interrupts get disabled. It was much better on the T3.6
We could play a bit with higher interrupt-priorities (for pin 0, too), just use a higher f_cpu, or a more sophisticated approach.

I assume, the problem is the output-side. The input is buffered by the audio-lib and not time-critical.

Edit:
For use with external connections, a low-pass on pin 1 will be helpful, i think.
 
Last edited:
I sent my recorded WAV file to the developer of the LTC_reader and LTC_convert software, just to see if I was doing something wrong with his software. He said my source LTC file was dirty. The volume fluctuations were no issue, but there was too much jitter or something. His list of TC from my file contained doubles / strange numbers.

Apparently his software is strict about validating incoming TC, which is why it failed to reliably recognize my signal.
Do you think the timing coming from the teensy isn't accurate enough? Or will that only become an issue with drift showing up in longer recordings?

I'll work on a RC low pass the the output on pin 1. I'm going to try and compare my generated signal with a known good signal.

-tom
 
It seems he uses a pretty picky algorithm (our code can do it better :) ) The signal is actually intended to pass through a wide variety of devices, to be filtered and tortured - and yet the decoding must work.
Okay, but as I said in the post before, I've seen that jitter/glitches too, and I think we can improve it.
First, I want to try to improve the int-priorities and higher cpu-speed - that's the easiest. (Btw, do you have a 3.6 at hand? can you test it?)
I'll download his Software.
 
Or will that only become an issue with drift showing up in longer recordings?

That's why it uses the sync-signal.. (pin 0) If you don't use it, it will drift away. That's natural - it's always the case when you compare two independent osciallators. There is no way to stop this without having some kind of mechanism.
So, if you want to use it for video - you really need this signal. The teensy is the "slave" (remember to set the pin 0 to INPUT), catches the signal and uses it as start-signal for a new LTC-frame. This way, there will be no drift.
So, we need some headroom to make this possible. Our timing has to be a tiny little bit faster (-> our "magic constant") , so we can wait for the video-sync to occur. If we are too slow, we might generate the last bit while the new frame starts. This is too late, and we must avoid this case. (i.e. if the real framerate is 24.001Hz we would run into problems if we are not a bit faster than 24.000 Hz)
There are other, more sophisticated ways to do that (a PLL for example) - i'll leave that for others.
 
Last edited:
Part of the problem is the parity function.

If you move the lines
Code:
...
  ltcTimer.begin(genLtc, ltcTimer_freq);
  ltcTimer.priority(0);
}

to the beginning of startLTC:
Code:
void startLtc() {

  ltcTimer.begin(genLtc, ltcTimer_freq);
  ltcTimer.priority(0);
....

it gets much better.

before:
pic_8_1.png

after:
pic_8_2.png

Maybe you want to try that. I have no time anymore this evening - maybe sunday.

edit:
- set all lines with ...Timer.priority(x); to ...Timer.priority(0);
- add NVIC_SET_PRIORITY(88, 0); //set GPIO-INT-Priority for Pin 1
- set theTeensy 3.2 to 96MHz
all this together reduces the jitter to less than a microsecond.

after that, you can reduce the "magic const" further. this will reduce the anomally of bit 0
 
Last edited:
I only have a Teesny 3.2 at the moment, so I can't try it on there, but I will try your code changes with my current setup and let you know how it goes.
I also borrowed another device to use for testing- it can both jam sync to incoming LTC and output LTC signal.

Apparently the LTC_reader software is used in syncing for the pyrotechnics industry, so he specifically designed it to be less friendly to poor incoming signals. Wouldn't want to set off a firework by accident!

Enjoy the rest of your week.

-Tom
 
First off I tested the running code on my teensy 3.2 with an external hardware LTC device, and it failed to sync to it. I haven't worked on the RC low pass filter yet, and that might be the problem. But I also looked at the serial output once more and noticed a potential error:
If you look at the numbering in the output here:
Code:
20:01:01.22
20:01:01.23
20:01:01.00
20:01:02.01
20:01:02.02
20:01:02.03

You'll notice that frame 00 is out of order, or rather, the frames change to zero a frame before seconds change. This would probably throw off any decoder that is looking for sequential frames, and it happens every 24 frames. I'm not sure this is a timing related problem- is it possible the LTC generation code has a bug that causes this?


Next I tried your modifications to the code for speed improvements, moving the 2 lines to the beginning of startLTC, and changing timer priority. That seemed to work fine, though it didn't change the out-of-order problem I described above. I also lowered the *magic number to 0.1 and set cpu speed to 96mhz.

When I added the NVIC_SET_PRIORITY(88, 0); the complier threw this error:
Code:
In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/core_pins.h:34:0,

                 from C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/wiring.h:38,

                 from C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/WProgram.h:45,

                 from C:\Users\TOM~1.TOM\AppData\Local\Temp\arduino_build_210495\pch\Arduino.h:6:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/kinetis.h:5665:50: error: expected unqualified-id before 'volatile'

 #define NVIC_SET_PRIORITY(irqnum, priority)  (*((volatile uint8_t *)0xE000E400 + (irqnum)) = (uint8_t)(priority))

                                                  ^

C:\Users\Tom.TomDell\Desktop\LTC reader\LTC readwrite\frankb_post_70_v2\frankb_post_70_v2.ino:18:1: note: in expansion of macro 'NVIC_SET_PRIORITY'

 NVIC_SET_PRIORITY(88, 0); //set GPIO-INT-Priority for Pin 1, added for speed improvement

 ^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/kinetis.h:5665:50: error: expected ')' before 'volatile'

 #define NVIC_SET_PRIORITY(irqnum, priority)  (*((volatile uint8_t *)0xE000E400 + (irqnum)) = (uint8_t)(priority))

                                                  ^

C:\Users\Tom.TomDell\Desktop\LTC reader\LTC readwrite\frankb_post_70_v2\frankb_post_70_v2.ino:18:1: note: in expansion of macro 'NVIC_SET_PRIORITY'

 NVIC_SET_PRIORITY(88, 0); //set GPIO-INT-Priority for Pin 1, added for speed improvement

 ^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/kinetis.h:5665:50: error: expected ')' before 'volatile'

 #define NVIC_SET_PRIORITY(irqnum, priority)  (*((volatile uint8_t *)0xE000E400 + (irqnum)) = (uint8_t)(priority))

                                                  ^

C:\Users\Tom.TomDell\Desktop\LTC reader\LTC readwrite\frankb_post_70_v2\frankb_post_70_v2.ino:18:1: note: in expansion of macro 'NVIC_SET_PRIORITY'

 NVIC_SET_PRIORITY(88, 0); //set GPIO-INT-Priority for Pin 1, added for speed improvement

 ^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/kinetis.h:5665:50: error: expected ')' before 'volatile'

 #define NVIC_SET_PRIORITY(irqnum, priority)  (*((volatile uint8_t *)0xE000E400 + (irqnum)) = (uint8_t)(priority))

                                                  ^

C:\Users\Tom.TomDell\Desktop\LTC reader\LTC readwrite\frankb_post_70_v2\frankb_post_70_v2.ino:18:1: note: in expansion of macro 'NVIC_SET_PRIORITY'

 NVIC_SET_PRIORITY(88, 0); //set GPIO-INT-Priority for Pin 1, added for speed improvement

 ^

Multiple libraries were found for "TimeLib.h"
 Used: C:\Program
Multiple libraries were found for "Audio.h"
 Used: C:\Program
Multiple libraries were found for "SPI.h"
 Used: C:\Program
Multiple libraries were found for "SD.h"
 Used: C:\Program
 Not used: C:\Program
Multiple libraries were found for "SerialFlash.h"
 Used: C:\Program
Multiple libraries were found for "analyze_ltc.h"
 Used: C:\Users\Tom.TomDell\Documents\Arduino\libraries\LinearTimecode-Decoder-master
Multiple libraries were found for "Wire.h"
 Used: C:\Program
Error compiling for board Teensy 3.2 / 3.1.
 
Just use the example from my github - I'll delete all other versions.

For the numbering: I don't think that this is a problem. IF it is, assume it as as a "TODO" for you or others.. :)
 
Ah, I had the NVIC in the wrong place. Now it complies fine.
Now I'm trying to understand the bitwise operators and the bit shifting you are doing.
I've got a lot to learn.
 
Hey Frank, when you set the initial data to 0, how come the hours are starting at 20 each time the Teensy reboots? I thought they would start at 0?
 
It uses the Teensy RTC. (Didn't you want that?)
So, it uses the hour(), minute(), second() function for the time.
If 20 was not the correct time, it's a bug, maybe.
There was bug with the parity. I've fixed it yesterday.

The initial data are for the user-bits and flags.
If you don't want the real time, you must add an offset.
 
I must have been confused about the starting time, because it does match the teensy RTC now. So when I get the external TXO connected, it should be relatively easy to use that for setSyncProvider. Maybe I was testing at the same time each night, and that's why I saw it always start with 20?! Or more likely, the Teensy RTC is resetting when I power cycle the teensy (as there is no battery currently). I added a line to set the teensy time, and that seems to work for setting the timecode:

setTime(10, 10, 0, 24, 4, 2020); //set initial timecode

There is still a problem with the output at the first frame, the seconds don't change when they should:
This is what it currently outputs (I'm set to 24fps):
20:53:17.23
20:53:17.00
20:53:18.01

It should output:
20:53:17:23
20:53:18:00
20:53:18:01
 
I - still - don't think that it is a problem, but ok, here it is.

Code:
void setup() {
#ifdef USE_LTC_INPUT
  AudioMemory(4);
#endif
  setSyncProvider(getTeensy3Time);
  pinMode(syncPin, OUTPUT);
  pinMode(ltcPin, OUTPUT);
  Serial.begin(9600);
  ltcTimer_freq = (1.0f / (2 * 80 * (fps ))) * 1000000.0f - 0.125f;// -0.125: make it a tiny bit faster than needed to allow syncing
  
  initLtcData();
[COLOR=#800000][I]  //set frame number to last frame to force roll-over with new second()
  ltc.data &= 0xf0f0f0f0f0f0fcf0ULL; //delete all dynamic data in frame
  int t, t10;
  t = fps;
  t10 = t / 10;
  ltc.data |= (t10 & 0x03) << 8;
  ltc.data |= ((t - t10 * 10));    
  //now wait for seconds-change 
  int secs = second();
  while (secs == second()) {;}  [/I][/COLOR]
  
  //start timer and pin-interrupt:
  fpsTimer.begin(&genFpsSync, (1.0f / (2 * fps)) * 1000000.0f);
  fpsTimer.priority(0);  
  attachInterrupt(digitalPinToInterrupt(syncPin), &startLtc, RISING);
  NVIC_SET_PRIORITY(88, 0); //set GPIO-INT-Priority for Pin 1
}

..but you still have to handle non-integer framerates and skipping. Your turn this time. It's your project :)
 
Last edited:
It works! and t's our project now :)

Seriously though, I really appreciate all your help to get it so far along, so fast. I also want to thank you for your patience and answering my many questions.

more to come.

-Tom
 
Hey folks,

Finally had a bit of time to play. Attached is an image. That's one of my generators feeding a Teensy 3.2 through the audio shield. It looks like the Teensy is a frame behind, which is definitely within my tolerance. This also might be that the generator is showing the frame that's being assembled. I will need to do further testing to be sure. But it works!!!

Next up I'm hoping to play a bit with frame rate calculations.

IMG_8002.jpg
 
Cool! Is this a I2C display? How fast is it?

You can reduce the lag by using a smaller audio block size.
Then, there is no check if the data are correct. You could add a check for the parity or if the received time makes sense.
I.e. a "time" 25:60:00 would be a indication for some bit errors..
 
Something is still wrong with the LTC output, but I'm not sure what it is.

I added a voltage divider and a RC circuit to the audio output to try and get a good signal out to my laptop for analysis.

Voltage divider is a 50K pot which I adjusted so incoming audio peaks around -12db
RC circuit is a 2.7k resistor and a 55.5nf cap that according to this calculator will create a 1khz low pass filter.

When I sent that signal into the LTCReader application it didn't work- it would occasionally read the incoming sync signal, but not for more than a few frames. Then I decided to run a test with a known good hardware LTC generator- a Tentacle Sync that I borrowed. I sent the audio from the Tentacle into the laptop and tried using LTCReader and it also didn't consistently read the incoming timecode. This makes me wonder if LTCReader is working right on my computer.

Next I tested using the teensy LTC sync generator to send LTC to the tentacle. This also didn't work, as the tentacle wouldn't sync up to the teensy audio output. This is with the audio set to a similar level as the tentacle's own LTC signal.

Finally, I tested sending LTC from the tentacle into the Teensy and that seemed to work. The serial output of the timecode reflected the tentacle's internal time.



I'm attaching audio files of the 2 sync signals in case anyone has any ideas. When I look at the waveforms they look similar, so I'm wondering if we are generating bad data that the Tentacle can't understand?

Teensy generated LTC audio file is here

Tentacle Sync generated LTC audio file is here
 
Last edited:
Good Morning.
.. I don't have any hard or software for that.
For a test we can run it a bit slower, say 2 frames/second and print the data ('1' or '0' the the serial monitor) and look if we see anything. I can not do more than that. I've never used LTC
I'll try this this evening. Or you can do it.
As it's own decoder runs good, it may be a wrong bit. The question is, which..
 
Last edited:
Back
Top