PDA

View Full Version : RangeFPS() - Change the tempo of a range in a clip


JohannesL
26th July 2009, 13:35
A little something I made to simplify speeding up or slowing down a part of a clip. I remember having trouble doing that when I was new to AviSynth, so I figured I'd post this.


function ChangePace(clip c, float fps)
{
return c.AssumeFPS(fps,true).ChangeFPS(c.FrameRate()).ResampleAudio(c.AudioRate())
}

function RangeFPS(clip c, float "new_rate", int "start", int "end")
{
Defined(new_rate) ? NOP() : Throw("RangeFPS: no new rate specified.")
start = Default(start,0)
end = Default(end,c.FrameCount()-1)

return c.Trim2(0,start) + c.Trim3(start,end+1).ChangePace(c.FrameRate()*new_rate) + c.Trim2(end+1,c.FrameCount)
}

__END__

Examples:

Make frames 300 to 500 play at double speed:
YourSourceHere()
RangeFPS(2.0, 300, 500)

Make the second half of the clip play at quarter speed:
YourSourceHere()
RangeFPS(0.25, start=FrameCount()/2)


Requires stickboy's jdl-util. (http://avisynth.org/stickboy/)

I might add more features later, like a blend mode using MVTools.

Gavino
26th July 2009, 14:30
Nice idea.

I have a few comments/questions/suggestions:
- why not allow start and end to be defaulted to 0 and framecount-1?
- why not have ChangePace accepting float fps, giving more accurate speed change?
- no need to use Eval here, it is redundant
- your frame numbers are out since the second frame parameter of Trim3 is exclusive
- also, Trim3 does not allow bounds beyond the end of the clip, so it fails for end = framecount-1; use Trim2 instead.
- using Trim2 for the first section means you don't have to special case start=0, and can use the same code for all cases:

c.Trim2(0,start) + c.Trim3(start,end+1).ChangePace(Round(c.FrameRate() * new_rate)) + c.Trim2(end+1,c.FrameCount)

JohannesL
26th July 2009, 15:00
Thanks for your feedback.
I tried this:
function RangeFPS(clip c, float "new_rate", int "start", int "end")
{
Defined(new_rate) ? NOP() : Throw("RangeFPS: no new rate specified.")
start = Default(start,0)
end = Default(end,c.FrameCount()-1)

c.Trim2(0,start) + c.Trim3(start,end+1).ChangePace(Round(c.FrameRate() * new_rate)) + c.Trim2(end+1,c.FrameCount)

return(last)
}
and now with RangeFPS(0.25) the last frame is displayed for 2 seconds at the end of the clip.

Gavino
26th July 2009, 15:37
Are you sure about that?

A useful tip for testing these sort of functions is to add ShowFrameNumber to the input clip, eg
ShowFrameNumber().RangeFPS(0.25)
That will show you exactly which original frame is being shown at each point.

Also, note that because of the rounding, the speed change will not be exact, eg at 25fps, 0.25 actually gives 0.24 (6/25) - hence my comment on ChangePace.
(This means the output will have more frames than you might expect, 25/6 times the original rather than 4 times.)

JohannesL
26th July 2009, 16:56
Oh! Sorry, there were already some dupes at the end of my test clip.
As for the float for ChangePace, I'm not sure how to do that, since AssumeFPS only takes ints.

Keiyakusha
26th July 2009, 17:30
AssumeFPS only takes ints.
No, it takes floats. It also takes num/den values. Sorry can't pick the right english word for it...

AssumeFPS (clip, float fps, bool "sync_audio")
AssumeFPS (clip, int numerator [, int denominator], bool "sync_audio")

JohannesL
26th July 2009, 18:20
Thanks for your help, Gavino and Keiyakusha. I updated the first post.

Gavino
26th July 2009, 18:41
You're welcome.

One final question: I'm curious why you use the line
Defined(new_rate) ? NOP() : Throw("RangeFPS: no new rate specified.")
You could also use Assert here:
Assert(Defined(new_rate), "RangeFPS: no new rate specified.")
But you could simply make the new_rate parameter mandatory by removing the quotes in the function declaration. Or is it a clever way to get a more specific error message instead of "Invalid arguments to function RangeFPS"?

JohannesL
26th July 2009, 20:20
I'm curious why you use the line
Defined(new_rate) ? NOP() : Throw("RangeFPS: no new rate specified.")
You could also use Assert here:
Assert(Defined(new_rate), "RangeFPS: no new rate specified.")
These two lines have the same effect, so I don't see the problem. Also, Assert seems unreliable - once my Assert(var >= 0,"msg") was ignored, but var >= 0 ? NOP() : Throw("msg") worked.
is it a clever way to get a more specific error message instead of "Invalid arguments to function RangeFPS"?
Yeah.

Gavino
26th July 2009, 21:25
These two lines have the same effect, so I don't see the problem. Also, Assert seems unreliable - once my Assert(var >= 0,"msg") was ignored, but var >= 0 ? NOP() : Throw("msg") worked.
Not a problem, just that Assert is what most people use and I have never known it not to work. It's so simple it can hardly fail, so I can only suspect you had something else going on in your script at the time, or misinterpreted the results.
Yeah.
Neat idea, thanks for the explanation.