Markers -> Time Remapped Stops... how?

Scripting for After Effects

Markers -> Time Remapped Stops... how?

Postby Laserschwert on 11/28/2014, 2:14 am

Hey there, I'm hoping you guys could help me out with a way to simplify my workflow via a script:

I'm very often creating 3D animations for our clients that need to stop at several points (a couple of seconds for a text overlay or something). To save on rendertime, you don't usually render out these held portions of the animation - instead you just stop and start the animation in post. Now, my usual workflow for these is to activate time remapping for the clip, creating a keyframe at each stop point, then manually spreading out the keys at those points (by going to the time of the stop, selecting that key and all that come after, moving them to the right and then copy pasting that first keyframe back to the current position). It works, but is quite tedious if you have to do it 10 or more times for several animations in a row.

What I had in mind was to automate this via a script (which is probably quite easy, but unfortunately I don't know anything about scripting). The workflow could be to just add markers to the layer - one at each frame that needs to be stopped - then running the script, which asks for the duration of the freeze frames, and then it automatically creates my time remap keys.

Does anybody here know how to do this?

Image
Last edited by Laserschwert on 11/28/2014, 4:37 am, edited 1 time in total.
Laserschwert
 
Posts: 38
Joined: 09/8/2008, 2:30 pm

Re: Markers -> Time Remapped Stops... how?

Postby al board on 11/28/2014, 2:47 am

You don't need any script, just save the time remap settings as an animation preset.
al board
 
Posts: 345
Joined: 05/3/2010, 5:22 am

Re: Markers -> Time Remapped Stops... how?

Postby Laserschwert on 11/28/2014, 2:52 am

al board wrote:You don't need any script, just save the time remap settings as an animation preset.

This would only work if the animations were all the same timing, etc. right? But I need this for pretty much all my projects, each requring different numbers of stops, different freeze durations, and are all different lengths. That's why I need a script, which ideally only requires my input in the form of markers and the desired freeze frame duration.
Laserschwert
 
Posts: 38
Joined: 09/8/2008, 2:30 pm

Re: Markers -> Time Remapped Stops... how?

Postby al board on 11/28/2014, 5:15 am

So... you're looking for this?
al board
 
Posts: 345
Joined: 05/3/2010, 5:22 am

Re: Markers -> Time Remapped Stops... how?

Postby Laserschwert on 11/28/2014, 5:31 am

Mh, not exactly... I think that expression is used to play a fixed range of the clip at a give marker. What I need is much, much simpler than that, as it just needs to create a few keys with some offset involved.
Laserschwert
 
Posts: 38
Joined: 09/8/2008, 2:30 pm

Re: Markers -> Time Remapped Stops... how?

Postby al board on 11/28/2014, 9:37 am

O.k. Sorry, but I'm afraid I cannot really be of assistance.
al board
 
Posts: 345
Joined: 05/3/2010, 5:22 am

Re: Markers -> Time Remapped Stops... how?

Postby dconklin on 12/1/2014, 11:17 am

Hello friend.

This script should help you out. It takes a user input of frames, reads markers on a layer, places keyframes w/ holds of input frames, offsets remaining keyframes and extends the layer.

I wrote this very quickly so there may be some kinks, but it has worked in my initial tests in CS6. It assumes you have a project open, a comp open and one layer selected.

Since these forums won't let me attach a .JSX, you'll have to take the code I'm posting and save it into a .JSX file. You can do this by opening up the extend script toolkit (included with your AE install – goes to utilities folder on a mac), paste this code in and save as a .JSX.

Once you have your JSX, simply do file > scripts > run script file and navigate to your .JSX. You could also place it into your Scripts or ScriptsUIPanels folder in your applications directory.

You can also point your Extend Script Toolkit to your version of AE (using the dropdown menu in the top left) and then run the script directly by pressing the 'play' button. This will also be helpful in debugging should any errors come up.

If you have trouble doing this, please PM me and I will find another way to get you the file.

Best of luck!

Code: Select all
{

   function holdAtMarkers(thisObj){

      function buildUI(thisObj){

         var w = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Hold At Markers");
            w.orientation = "row";

            w.add("statictext", undefined, "Hold Time (Frames):");
            var theTime = w.add("edittext", undefined, "20");
               theTime.characters = "5";

            var okButton = w.add("button", undefined, "Do It!");
            var cancelButton = w.add("button", undefined, "Cancel");

            okButton.onClick = function(){
               app.beginUndoGroup("Hold At Markers");
                  doHolds(theTime.text);
               app.endUndoGroup();
            }

            cancelButton.onClick = function(){
               w.close();
            }


            w.layout.layout(true);
            return w;

      }

      function doHolds(holdTime){

         var proj = app.project;
         var ai = proj.activeItem;
         var theLayer = ai.selectedLayers[0];

         if (proj == null) { return alert("Please open a project before using this script!"); }
         if (ai == null) { return alert("Please have a comp open before using this script!"); }
         if (theLayer == null) { return alert("Please select one layer before using this script."); }

         var mkrs = theLayer.property("Marker");

         if (mkrs.numKeys == 0) { return alert("No markers on selected layer. Did you set comp markers by mistake?"); }

         var getTimes = [];
         for(var i = 1; i <= mkrs.numKeys; ++i){
            getTimes.push(mkrs.keyTime(i));
         }

         //enable time remapping
         theLayer.timeRemappingEnabled = true;

         //format hold to frames
         var holdFrames = holdTime * ai.frameDuration;

         //get value to fix out point.
         var lastKeyVal = theLayer.timeRemap.keyValue(2);

         //find out time from last marker to laye end.
         var endDif = theLayer.outPoint - getTimes[getTimes.length - 1];
         
         //remove last key (as to not overlap)
         theLayer.timeRemap.removeKey(2);

         //set keys
         for(var i = 0; i < getTimes.length; ++i){
            theLayer.timeRemap.setValueAtTime(getTimes[i] + (holdFrames * i), getTimes[i]);
            theLayer.timeRemap.setValueAtTime(getTimes[i] + (holdFrames * i) + holdFrames, getTimes[i]);
         }

         //fix end
         theLayer.outPoint = theLayer.timeRemap.keyTime(theLayer.timeRemap.numKeys) + endDif;
         theLayer.timeRemap.setValueAtTime(theLayer.outPoint, lastKeyVal);

         if(theLayer.outPoint > ai.displayStartTime + ai.duration){
            alert("Finished, but layer end point may go past comp end time. Please check it.");
         }

         writeLn("Hold at Markers Finished!");
      }

      var theWindow = buildUI(thisObj);
      if(theWindow instanceof Window){
         theWindow.center();
         theWindow.show();
      } else {
         theWindow.layout.layout(true);
      }


   }

   holdAtMarkers(this);


}
dconklin
 
Posts: 19
Joined: 03/23/2011, 6:24 am

Re: Markers -> Time Remapped Stops... how?

Postby Laserschwert on 12/3/2014, 8:45 am

Thank you so much! That's exactly what I needed :)

One thing I noticed though is that Time Remapping needs to be active for the layer in question, and the script doesn't have a failsafe for that. Maybe the script should just activate the time remapping itself if it's off, or warn the user that the existing time remapping would be replaced if he continues with the script. It's no biggie, but would just be the icing on the cake of the script ;)

But even now, thanks a lot for you help!

EDIT: Sorry, I just saw the "theLayer.timeRemappingEnabled = true;" line, which should obviously do what I mentioned. I'm using the German version of AE, so maybe that's a problem here.

EDIT2: I think I've found the problem: The parameter for time remapping is actually "timeRemapEnabled", so it was just a typo on your end ;) But still there should be a warning, if time remapping is already activated (and has keys?). Right now I've just put a "timeRemapEnabled = false" first, so that existing keys get deleted, no matter what.

EDIT3: And another problem I found: The last key is way off in it's position... it's like a multiple of the comp length away from the penultimate key. So maybe somethings wrong with the "find out time from last marker to layer end"-block?
Laserschwert
 
Posts: 38
Joined: 09/8/2008, 2:30 pm

Re: Markers -> Time Remapped Stops... how?

Postby dconklin on 12/3/2014, 10:41 am

Hey there.

Thanks for catching that typo, my fault!

I was able to replicate the error you're describing – it seems to only come into play if you have a layer that was time-remapped and then un-time remapped, as the time-remap somehow retains the info of the out point during the previously time-remapped state. To fix this, I've added a line which sets an explicit out point at the last keyframe when time remapping is initially enabled (line 65).

Give this a go and let me know – I've also fixed the typo. You shouldn't need to check whether time remapping is enabled, since theLayer.timeRemapEnabled=true will turn on time remap if it's off and do nothing if it's on already.

Sorry about the goof!

Edit: Added a confirm dialog box to remove any existing keyframes which may cause problems.

Code: Select all
{

   function holdAtMarkers(thisObj){

      function buildUI(thisObj){

         var w = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Hold At Markers");
            w.orientation = "row";

            w.add("statictext", undefined, "Hold Time (Frames):");
            var theTime = w.add("edittext", undefined, "20");
               theTime.characters = "5";

            var okButton = w.add("button", undefined, "Do It!");
            var cancelButton = w.add("button", undefined, "Cancel");

            okButton.onClick = function(){
               app.beginUndoGroup("Hold At Markers");
                  doHolds(theTime.text);
               app.endUndoGroup();
            }

            cancelButton.onClick = function(){
               w.close();
            }


            w.layout.layout(true);
            return w;

      }

      function doHolds(holdTime){

         var proj = app.project;
         var ai = proj.activeItem;
         var theLayer = ai.selectedLayers[0];

         if (proj == null) { return alert("Please open a project before using this script!"); }
         if (ai == null) { return alert("Please have a comp open before using this script!"); }
         if (theLayer == null) { return alert("Please select one layer before using this script."); }

         var mkrs = theLayer.property("Marker");

         if (mkrs.numKeys == 0) { return alert("No markers on selected layer. Did you set comp markers by mistake?"); }

         var getTimes = [];
         for(var i = 1; i <= mkrs.numKeys; ++i){
            getTimes.push(mkrs.keyTime(i));
         }

         //enable time remapping
         theLayer.timeRemapEnabled = true;

         //alert if time remap already has keys
         if (theLayer.timeRemap.numKeys > 2) {
            var hasKeysPrompt = confirm("You already have set time remapping keys. This script may yield unexpected results. Would you like to remove these keyframes before continuing?");
            if(hasKeysPrompt){
               theLayer.timeRemapEnabled = false;
               theLayer.timeRemapEnabled = true;
            }
         }

         //set out point properly
         theLayer.outPoint = theLayer.timeRemap.keyTime(theLayer.timeRemap.numKeys);

         //format hold to frames
         var holdFrames = holdTime * ai.frameDuration;

         //get value to fix out point.
         var lastKeyVal = theLayer.timeRemap.keyValue(2);

         //find out time from last marker to laye end.
         var endDif = theLayer.outPoint - getTimes[getTimes.length - 1];
         
         //remove last key (as to not overlap)
         theLayer.timeRemap.removeKey(2);

         //set keys
         for(var i = 0; i < getTimes.length; ++i){
            theLayer.timeRemap.setValueAtTime(getTimes[i] + (holdFrames * i), getTimes[i]);
            theLayer.timeRemap.setValueAtTime(getTimes[i] + (holdFrames * i) + holdFrames, getTimes[i]);
         }

         //fix end
         theLayer.outPoint = theLayer.timeRemap.keyTime(theLayer.timeRemap.numKeys ) + endDif;
         theLayer.timeRemap.setValueAtTime(theLayer.outPoint, lastKeyVal);

         if(theLayer.outPoint > ai.displayStartTime + ai.duration){
            alert("Finished, but layer end point may go past comp end time. Please check it.");
         }

         writeLn("Hold at Markers Finished!");
      }

      var theWindow = buildUI(thisObj);
      if(theWindow instanceof Window){
         theWindow.center();
         theWindow.show();
      } else {
         theWindow.layout.layout(true);
      }


   }

   holdAtMarkers(this);


}
dconklin
 
Posts: 19
Joined: 03/23/2011, 6:24 am

Re: Markers -> Time Remapped Stops... how?

Postby Laserschwert on 12/4/2014, 5:44 am

That's it! Wonderful, thanks again :)

The only thing I've changed is "if (theLayer.timeRemap.numKeys > 0)", because even if there are only 2 existing time-remap keys, it doesn't mean that those are the normal start and end keys. So I think it's better to just delete them anyway.
Laserschwert
 
Posts: 38
Joined: 09/8/2008, 2:30 pm


Return to After Effects Expressions