AviSynth Syntax - Control structures¶
In the strict sense, AviSynth Syntax provides four control structures
(actually five, the fifth being the conditional operator, ?:
, presented
elsewhere), the try..catch
statement, the if..else
statement, and the while
and for
loop.
The try..catch
statement¶
The try..catch
statement permits the execution of code that may
generate a run-time error and the handling of the error, if it actually
arises.
The full syntax of the try..catch
statement is:
try {
...
statements
...
}
catch(err_msg) {
...
statements
...
}
The err_msg
string in the catch
block contains the text generated by
AviSynth when the error inside the try
block was encountered. This text
is the same that would appear in the familiar MessageBox that shows up when a
fatal script error occures.
You can query the text (it is a normal string variable) to find specific substrings inside that indicate the error that has been encountered. This is a technique that can produce many useful results (for an example, see here).
The if..else
statement¶
Adapted from Gavino's GScript plugin.
The full syntax of the if
or if..else
statement is:
if ( condition ) {
...
statements
...
}
else {
...
statements
...
}
where condition
is any boolean expression.
The statements can be any Avisynth statements, including the control structures themselves, and
so the new constructs can be nested.
The else part may be omitted (equivalent to else {}).
if ( condition ) {
...
statements
...
}
The 'else if' construct is also provided (optionally repeated to create a chain of conditionals).
if ( condition ) {
statements
}
else if ( condition ) {
statements
}
else if (...) { ... }
...
else {
...
statements
...
}
The while
loop¶
Adapted from Gavino's GScript plugin.
The full syntax of the while
loop is:
while ( condition ) {
...
statements
...
}
where condition
is any boolean expression.
The statements are repeated while the condition is true.
Example:
while (Height() < h) {
StackVertical(last, c)
}
break
and continue
can be used as well (see example later) - available from v3.7.4
The for
loop¶
Adapted from Gavino's GScript plugin.
The full syntax of the for
loop is:
for ( variable = init , limit , step ) {
...
statements
...
}
init
, limit
and step
are integer expressions, with step
non-zero. step
is optional and defaults to 1.
First the variable
is set to the value of init
.
The statements are repeated until the exit condition is met,
ie variable
exceeds limit
(if step
> 0), or is less then limit
(if step
< 0).
After each iteration, variable
is incremented by step
.
If the initial value satisfies the exit condition, the number of iterations will be zero.
Example:
for (i=1, nBlurs) {
Blur(0.5)
}
break
and continue
can be used as well (see example later) - available from v3.7.4
Using break
and continue
in loops¶
break
and continue
are used to control the flow of loops.
The break statement is used to terminate the loop prematurely when a certain condition is met and control is transferred to the next statement after the loop.
The continue statement skips the current iteration and control is transferred to the beginning of the loop for the next iteration.
ColorBars()
for (i=1, 6) {
SubTitle(Format("statement_1 i={i}"), x=0, y=i*20)
SubTitle(Format("statement_2 i={i}"), x=140, y=i*20)
if(i == 4) {
SubTitle(Format("Break at i={i}"), x=280, y=i*20)
break # Terminates the loop when i equals 4
}
SubTitle(Format("statement_3 i={i}"), x=280, y=i*20)
if(i == 2) {
SubTitle(Format("Continue at i={i}"), x=420, y=i*20)
continue # Skips the current iteration when i equals 2
}
SubTitle(Format("Final statement in i={i}"), x=420, y=i*20)
}
SubTitle(Format("Outside"), x=0, y=7*20)
results in the output:
statement_1 i=1 statement_2 i=1 statment_3 i=1 Final statement in i=1
statement_1 i=2 statement_2 i=2 statment_3 i=2 continue at i=2
statement_1 i=3 statement_2 i=3 statment_3 i=3 Final statement in i=3
statement_1 i=4 statement_2 i=4 Break at i=4
Outside
Other control structures (in the broad sense)¶
In the broad sense, there are many elements inside AviSynth Syntax that although not control structures by themselves, together they allow the creation of language constructs equivalent to a control structure. Those constructs in turn allow the performance of complex programming tasks.
The elements under consideration are the following:
The
Eval()
statement that allows execution of arbitrary script language statements (and its cousinApply
that simplifies calling functions by name).Multiline strings and in particular multiline strings surrounded by triple double quotes (the
"""
sequence of chars), since they allow to write string literals inside them naturally, as one would do in a normal AviSynth script.The
Import()
statement that allows execution of arbitrary scripts, which can return a value (not necessarily a clip; a script can return a value of any type, which can be assigned to a variable of the calling script).Recursion (the ability to create recursive functions).
Control functions, in particular Assert, Select, Default, NOP.
The first three allow one to create simple Block statements, such as
branching blocks (the analogous of the if..elseif..else
control structure
commonly encountered in programming and scripting languages). A basic example
is presented below (see the link above for more):
# define different filtering based on this flag
heavy_filtering = true
AviSource("c:\sources\mysource.avi")
# assign result of Eval() to a variable to preserve the value of the
last special variable
dummy = flag ? Eval("""
Levels(0, 1.2, 255, 20, 235)
stext = "heavily filtered"
Spline36Resize(720, 400)
""") : Eval("""
stext = "lightly filtered"
BicubicResize(720, 400)
"""
AddBorders(0, 40, 0, 40)
Subtitle(stext)
The fourth is the general tool provided by the AviSynth syntax for operating on collections and calculating expressions of any complexity. It is also, currently, the only tool.
This does not mean that there is something that you can't do inside the AviSynth script language; in fact recursion together with assignment can achieve everything an imperative language with looping constructs can do. It just does it in a way that most people without special programming skills are not familiar, since functional programming is not a major ICT course but rather a specialised topic.
The fifth are more or less necessary tools inside the above constructs for controlling input, setting values and facilitating proper execution of the construct.
Lets look at some examples of recursion, to grasp the general pattern that one must follow to master it for its purposes.
Example 1: Create a function that returns a n-times repeated character sequence¶
We will use an existing implementation, from AVSLib as our example:
Function StrFill(string s, int count, bool "strict") {
strict = Default(strict, true)
Assert((strict ? count >= 0 : true), "StrFill: 'count'
cannot be negative")
return count > 0 ? s + StrFill(s, count - 1) : ""
}
The recursion is the call that the function makes to itself at the return
statement. In order to be done properly, the sequence of recursive calls must
eventually end to a single return value. Thus a recursive function's return
statement will always use the conditional operator, ?:
.
This is all that is about recursion, the other two lines (where the fifth element, control functions are used) are simply for ensuring that proper arguments are passed in. The "strict" argument is just and add-on for using the functions in case where it should quietly (without throwing an error) return an empty string.
Example 2: Create a function that selects frames of a clip in arbitrary intervals¶
Filters like SelectEvery allow the efficient selection of arbitrary sets of frames. They require though that each set of frames has a constant frame separation with its successor and predecessor set (in other words, the sets are periodic on frame number). In order to select frames with varying separation (that is non-periodic) we have to resort to script functions that use recursion.
The function below is a generic frame selection filter, which in order to
select arbitrary frames uses a user-defined function (the func
argument
must contain its name) that maps the interval [s_idx..e_idx)
to the set
of frames that will be selected. func
must accept a single integer as
argument and return the corresponding mapped frame number.
Function FSelectEvery(clip c, string func, int s_idx, int e_idx) {
Assert(s_idx >= 0, "FSelectEvery: start frame index
(s_idx) is negative")
f = Apply(func, s_idx)
return (s_idx < e_idx && f >= 0 && f < c.Framecount) \
? c.Trim(f, -1) + FSelectEvery(c, func, s_idx + 1,
e_idx) \
: c.BlankClip(length=0)
}
The recursive step (first conditional branch in the return
statement) is
again an expression that involves the function as a subpart. This is not
necessary in the general case (depending on the specific task, it could also
be just the function call) but it is the most usual case when building
complex constructs.
Apply
calls the user function to calculate the frame number (a more
robust implementation would enclose this call in a try...catch
block). If
the frame number is within the clip's frames then the associated frame is
appended to the result else recursion ends.
The following example will clarify the design:
# my custom selector (x^2)
Function CalcFrame(int idx) { return Int(Pow(idx, 2)) }
AviSource("my_200_frames.avi")
# select up to 20 frames, mapped by CalcFrame
# in this case: 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144,
169, 196
FSelectEvery(last, "CalcFrame", 0, 20)
Changelog¶
Version |
Changes |
---|---|
Avisynth+ 3.7.4 |
Added "break" and "continue |
$Date: 2024/04/10 9:38:00 $