Copy link to clipboard
Copied
I'm trying to write an expression that gets the value of a property for each of the next 100 frames and determine if they're all below a certain value. If they are, change a boolean to true and vice versa. Is this possible in the After Effects paradigm? Thanks.
Copy link to clipboard
Copied
Sure. A simple for() loop will do.
Mylenium
Copy link to clipboard
Copied
If you are trying to look forward from the current time to the next 100 frames, you are going to end up with a recursive expression that slows down the farther down the timeline you go. The slowdown can be exponential. You will have to take time and create a loop that looks forward 100 * thisComp.frameDuration and looks for your critical value. It can throw a switch or change a color if it finds it and returns a false value.
Add this expression to a text layer above a layer with an animated position property, and it will tell you whether the layer is above or below the center of the comp 100 frames ahead of the current time.
lookForward = 100 * thisComp.frameDuration;
v = thisComp.layer(index + 1).transform.position.valueAtTime(time + lookForward);
trgtVal = thisComp.height/2;
if (v[1] < trgtVal){
p = ": Top"
}
else{
p = ": Bottom"
}
"Position" + p
It won't compare all 100 frames, but it would look ahead to detect the change.
If I get some free time later on, I will take a look at adding a loop operator to check if any frame is above the target value.
Copy link to clipboard
Copied
Something like this, probably (this example is for the layer's rotation property):
prop = rotation;
threshold = 20;
val = true;
for (i = 0; i < 100; i++){
if (prop.valueAtTime(time + framesToTime(i)) >= threshold){
val = false;
break;
}
}
val
Copy link to clipboard
Copied
Thank you for the help, Dan. This worked! Would you mind helping me with the second aspect of this project, which is rotating a camera 90º if the boolean is true, and back to 0º if the boolean is false, but using ease() to make the rotation happen over time? Is this possible? Here's what I've tried and ease() isn't kicking in:
t = timeToFrames(time);
tFor = t + 48;
startRot = 0;
endRot = 90;
if (val){
ease(time,t,tFor,startRot,endRot);
} else if (! val) {
ease(time,t,tFor,endRot,startRot);
}
Copy link to clipboard
Copied
Paul, that changes things. In that case the expression needs to figure how far into the future the threshold crossing occurs and use that to drive the ease expression. So that's probably going to look somethng like this:
prop = rotation;
threshold = 20;
startRot = 0;
endRot = 90;
if (prop.value >= threshold){
endRot;
}else{
for (i = 1; i <= 100; i++){
if (prop.valueAtTime(time + framesToTime(i)) >= threshold){
break;
}
}
ease(i,1,100,endRot,startRot);
}
Copy link to clipboard
Copied
Thank you for your help, Dan. If you wouldn't mind one more imposition, I was wondering if you could look at the entire expression in context and tell me where my logic is wrong.
I've analyzed an audio file of two people talking and am using the amplitude keyframes of one of the channels to determine when to pan a 3D camera to a picture of the two speakers. This expression lies in the Y Rotation property of the 3D camera. The rotation value changes as expected, but the ease() functions don't work. Here is the expression:
//variables for determining if Jon is talking
jon = thisComp.layer("Audio Amplitude").effect("Jon Channel")("Slider");
threshold = 3;
jonHush = false;
lookAhead = 65;
counter = 0
//variables for rotation
startRot = 0;
endRot = 90;
durRot = 96;
//determines if Jon is talking
for (i = 0; i < lookAhead; i++){
if (jon.valueAtTime(time + framesToTime(i)) < threshold){
counter++;
if (counter >= lookAhead){
jonHush = true;
}
} else {
jonHush = false;
counter = 0;
break;
}
}
//rotates camera on Y axis according to if Jon is talking
if (! jonHush){
ease(time,timeToFrames(time),timeToFrames(time)+durRot,endRot,startRot);
} else if (jonHush){
ease(time,timeToFrames(time),timeToFrames(time)+durRot,startRot,endRot);
}
Copy link to clipboard
Copied
This is actually a pretty complex problem, but basically your expression needs to first determine the current speaker. Then use a loop to look ahead to see if the speaker changes within the next 65 frames. If so, ease the rotatation based on how many of the 65 frames have already elapsed. Your expression, is missing a number of pieces to make that happen (as is the last one that I provided, which didn't include the logic to ease back and forth between conditions). It's do-able, but a bit of work.
Copy link to clipboard
Copied
Thinking about this some more--it's even more complex than I thought. If the expression wakes up in the middle of a gap of silence, it needs to first figure out if that gap is long enough to have triggered a rotation, which could require examining previous and future frames. All of this maneuvering is required because of the limitation that expressions have no memory--an expression can't set anything that it can detect at a future (or previous) frame. So an expression can't leave behind a flag that says "we're currently in a speech gap"--it has to figure that out on every frame.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
No, the expression would use different logic, depending on whether it wakes up above or below the threshold. Abovc, it needs to look forward to see if there is a gap coming of such duration that it needs to be rotating. Below, it needs to look forward and backward to see if it's currently in a gap sufficient to ease to (or keep) max rotation, or if it's getting near the end of a gap and needs to be rotating back to the speaker. It's absolutely do-able, just tricky. There are a lot of cases it has to account for, like gaps not sufficient to cause rotation, or possibly short bursts of speech where there isn't really time to rotate back. I think you need to start with a good spec that covers what you want to happen in all situations.
Copy link to clipboard
Copied
Dan, how would you use a property in an array like Scale or Position? When I try position[0] instead of rotation I get an error. The only way to not break the expression is to create an array for threshold, but I only want to look at the Y position.
Paul, you have to add an argument that does something when the val changes from true to false. Something like this;
You'll then need to work out a time when the "val" changes from true to false and use that to drive an interpolation method like ease(tome, time of the change, time of the change + duration, start value, end value).
Dan, you can see the problem in the threshold line of the expression. I only want to look at the Y value.
ref = thisComp.layer(index + 1);
prop = ref.position;
threshold = [700, 540];
val = true;
for (i = 0; i < 100; i++){
if (prop.valueAtTime(time + framesToTime(i)) >= threshold){
val = false;
break;
}
}
val
if (val == true){
a = "Do Something"
}
else{
a = "Do something else"
}
Copy link to clipboard
Copied
Paul, you have to add an argument that does something when the val changes from true to false. Something like this;
You'll then need to work out a time when the "val" changes from true to false and use that to drive an interpolation method like ease(tome, time of the change, time of the change + duration, start value, end value).
By @Rick Gerard
Hi, Rick. Thanks for the help. Have I not implemented what you suggested in the code in my last post?
Copy link to clipboard
Copied
Paul, You kind of have the if/else, but you have not supplied any time values for the ease interpolation.
The for loop is also broken if there are more than 2 keyframes and the last one is greater than the threshold.
Copy link to clipboard
Copied
Rick, here's how I'd set it up for y position:
prop = position;
threshold = 540;
val = true;
for (i = 0; i < 100; i++){
if (prop.valueAtTime(time + framesToTime(i))[1] >= threshold){
val = false;
break;
}
}
a = val ? "Do Something" : "Do Something Else"
Copy link to clipboard
Copied
Thanks, Dan. I was fouling up the placement of the [1].
There's another problem. If the property is keyframed, the switch only occurs if the last value is less than the threshold. Only the last keyframe is used. If the last keyframe is greater than the threshold nothing changes. I've tried adding OR arguments with no luck.
It doesn't matter what property. The switch is only thrown when the value decreases in the last keyframe.
Copy link to clipboard
Copied
I think the issue is that it's always looking up to 100 frames (3.33 seconds) into the future. You'd need a stretch of longer than that where the threshold isn't reached to see any change.
Copy link to clipboard
Copied
You got it, Dan. I have been at the computer for about 12 hours and thought my timeline was 8 minutes, not 8 seconds.
This expression is going to be useful in a bunch of MGRTS I have already created. Dan's the Man.
Copy link to clipboard
Copied
Heya Dan! I think it'll work to just get the info as it without having to look forward. Once all the values have been captured, the user can timeoffset the required keyframes. Correct?
Copy link to clipboard
Copied
Hi Roland--sorry, I'm not tracking what you're suggesting. What do you mean, exactly?
Copy link to clipboard
Copied
I THINK, the entire reason for looking ahead 100 frames is for the user to move/rotate the camera into position and the target is the audio/voice file. The Look-Ahead is to know when to more the camera and to which speaker. So, all the required keyframes can be captured in current time with AE. When the capture is done, the user can move the cam keyframes earlier in time; mimicking the desired result of the Look-Ahead.
Copy link to clipboard
Copied
The keyframes you're talking about are the audio-converted-to-keyframes, is that correct? I think the expression looking at those has to make some decisions, based on the duration of any silent or sound patches, compared to what's happened recently and what's coming up. I don't see any way around that, but I may be missing something obvious (wouldn't be the first time).
Copy link to clipboard
Copied
My thoughts are to capture something 100 frames ahead in time is the same as capturing at current time and then moving these keyframes back in time by 100 frames.
Sense Make?
Copy link to clipboard
Copied
No man, I'm sorry, I don't get it. If the expression is looking at a copy of the keyframes shifted in time by 100 frames, I don't see how that would change the quantity of frames that it would have to examine, at all. It would just have to adjust all its valueAtTime() queries by 100 frames.
Copy link to clipboard
Copied
I'm looking at simplifying the problem. So, everything gets captured at current time instead of 100 frames ahead. Then after capturing the values and the keyframes are created, one can manually move these keyframes earlier in time or use valueAtTime. The result should be the same but a lot quicker to process the initial expressions if the created keyframes are manually moved 100 frames earlier in time.
Find more inspiration, events, and resources on the new Adobe Community
Explore Now