Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Triggering text animators with markers

Community Beginner ,
Oct 24, 2022 Oct 24, 2022

Hi everyone!

 

I'm trying to animate captions using sourceText keyframes for each sentence, which I want to animate on a per-word basis using layer markers as the triggers, so that the text is synced up with the audio.

Screenshot 2022-10-24 140818.png

 

What I've found so far is using the expression below (thanks ukramedia) on a text animators expression selector, which works beautifully until the next sourceText keyframe, then it resets. 

anim = thisProperty;
delayDur = framesToTime(effect("Delay Duration")("Slider").value);
numMarkers = marker.numKeys;
delay = (textIndex - 1)*delayDur;

if (numMarkers > 0)
{delay = textIndex <= numMarkers ? marker.key(textIndex).time : marker.key(numMarkers).time;}

anim.valueAtTime(time - delay);

 

Now, my idea is to reset the text animation every time there's a sourceText keyframe, but I just cannot wrap my head around on how to integrate this into an if/else expression.

Do you have any ideas how to approach this? Your help would be highly encouraging 

 

Best

Julian

 

 

 

TOPICS
Expressions
897
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Beginner , Oct 24, 2022 Oct 24, 2022

Hey again, I just got help and this is the final code. It works flawlessly. 

anim = thisProperty;
delayDur = framesToTime(effect("Delay Duration")("Slider").value);
numMarkers = marker.numKeys;
txt = text.sourceText;
delay = (textIndex - 1) * delayDur;


for (i = 0;
    (i < txt.numKeys && txt.key(i + 1).time <= time); i++) {
    // dummy loop, i counts # of sourceText Keys that we have passed
};
for (j = 0;
    (i > 0 && j < marker.numKeys && marker.key(j + 1).time < txt.key(i).time); j++) {
   
...
Translate
LEGEND ,
Oct 24, 2022 Oct 24, 2022

You need to run it in a loop to actually check each marker. The expression you are using simply assumes a fixed delay and will simply snap to the next keyframe or the last one it encounters. There's no actual logic here that compares the keyframes and markers. You need to check which key and marker are nearest to your current time and then use that as the trigger. Refer to Dan Ebberts' classic code to get an idea:

 

http://www.motionscript.com/design-guide/marker-sync.html

 

Mylenium

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Oct 24, 2022 Oct 24, 2022

Thanks for your kind input, Mylenium.

 

I should've said that I'm a total beginner to expressions and that I've hit a dead end after trying to figure this out for the last couple of days. I've actually already tried some code by Dan, but only got it to work on range selectors. This is the code:

m = text.sourceText;
if (m.numKeys > 0){
  n = m.nearestKey(time).index;
  if (m.key(n).time > time) n--;
}
if (n > 0){
  t1 = m.key(n).time;
  if (n < m.numKeys){
    t2 = m.key(n+1).time;
  }else{
    t2 = t1 + .5;
  }
  ease (time,t1,t2,0,100);
}else
  0

 

This works beautifully, but since I'd love to be able to sync up the words with the audio, I need to migrate this into the other expression based on markers. Can you further assist me with this? Would love to understand this better. 

 

Thanks again

Julian

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Oct 24, 2022 Oct 24, 2022

Looking at your original source image I think I misunderstood your request, at least in part. The delay from the Dan Ebberts example seems irrelevant, but the logic for the keyframes and markers still applies. In your case you would basically use the methodology twice in a nested construct. The outside loop would be the keyframes and the inside the markers where you substitute the normal key with markerKey and create another variable for marker.numKey that equates the m variable. I don't have time to sit down right now to test this, but perhaps you can figure it out based on my crude advice.

 

Mylenium

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Oct 24, 2022 Oct 24, 2022

Appreciate the effort, thanks for taking your time. I'm trying to implement your advise, but can't seem to crack the code. 

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Oct 24, 2022 Oct 24, 2022
LATEST

Hey again, I just got help and this is the final code. It works flawlessly. 

anim = thisProperty;
delayDur = framesToTime(effect("Delay Duration")("Slider").value);
numMarkers = marker.numKeys;
txt = text.sourceText;
delay = (textIndex - 1) * delayDur;


for (i = 0;
    (i < txt.numKeys && txt.key(i + 1).time <= time); i++) {
    // dummy loop, i counts # of sourceText Keys that we have passed
};
for (j = 0;
    (i > 0 && j < marker.numKeys && marker.key(j + 1).time < txt.key(i).time); j++) {
    // dummy loop, j counts # of markers that have occured before the current sourceText Keyframe.
}

textIndex += j; // offset the textIndex so we relatively start counting from 1 after each txt keyFrame

if (numMarkers > 0) {
    delay = textIndex <= numMarkers ? marker.key(textIndex).time : marker.key(numMarkers).time;
}
anim.valueAtTime(time - delay);

 

Super stoked this is possible. Thanks again for the guidance! 

 

Best

Julian

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines