Arrays

Arrays (AviSynth+)

Beginning Avisynth+ 3.6 script arrays are supported. Functionality is different from AVSLib described below.

  • Arrays can have one or more dimensions

  • They even can be empty.

  • AVSValue (internal representation of any Avisynth variable) is deep copied for arrays (arrays in arrays in ...) (note: this is true when using them through c++ plugins. C interface is different, there is no deep-copy there)

  • untyped and unconstrained element number

  • arrays can appear as internal filter parameters (named and unnamed).

  • arrays can appear as user defined function parameters (named and unnamed).

  • elements are accessible with indexes or in a dictionary-like associative way

  • Array modifier functions allow multidimensional subarray indexes

  • array indexes start from zero.

  • ArrayIns - insert before position

  • ArrayAdd - append

  • ArrayDel - delete from position

  • ArraySet - replace at position

  • ArrayGet - a function-like version of the bracket-type [] syntax

  • ArraySort - sort arrays of bool, numeric or string values

  • Query the array length with ArraySize

  • Test is a variable is of explicite array type: IsArray

ArrayIns

ArrayIns(array_to_mod, value_to_insert, index1 [, index2, index3...])

Insert a value into an array or into its subarray.

Returns a new array with value_to_insert inserted into array_to_mod (1D array) or array_to_mod[index1 (, index2, index3...)] (multi-dimensional array).

The indexes point to the insertion point. Index 0 will insert at the beginning of the array. Index (ArraySize) will insert after the last element (same as ArrayAdd - append) Original array (as with the other functions) remains untouched.

ArrayAdd

ArrayAdd(array_to_mod, value_to_append [, index1, index2, index3...])

Appends value to the end of an array or its subarray.

Returns a new array with value_to_append appended to array_to_mod (1D array) or array_to_mod[index1 (, index2, index3...)] (multi-dimensional array). Original array (as with the other functions) remains untouched.

ArrayDel

ArrayDel(array_to_mod, index1 (, index2, index3...])

Returns a new array in which the requested position was deleted. Original array (as with the other functions) remains untouched.

ArraySet

ArraySet(array_to_mod, replacement_value, index1 [, index2, index3...])

Returns a new array with array_to_mod[index1 (, index2, index3...)] = replacement_value

Original array (as with the other functions) remains untouched.

ArrayGet

ArrayGet(array, index1 [, index2, index3...])

A function version of accessing an array element.

During the script parsing the bracketed syntax is silenly converted to ArrayGet.

Example

# these are the same
x = a[1]
x = ArrayGet(a, 1)
x = a.ArrayGet(1)
n = b[3, 2]
n = ArrayGet(b, 3, 2)
n = b.ArrayGet(3, 2)

ArraySize

int ArraySize(array_value)

Returns the size of the parameter. For getting the size of a subarray, pass the inner element.

ArraySort

ArraySort(array_to_sort)

Sorts an one dimensional array of bool, numeric or string values.

Returns a new sorted array. Different basic types cannot be mixed inside the array, e.g. elements must be all bools, numbers or strings. Integer and floating point values can be mixed however.

The original array remains untouched.

a = ["banana", "apple", "lemon", "aardvark"]
b = arraySort(a)
for (i=1, ArraySize(b)) {
  SubTitle(Format("Element i={i} = {0} ", b[i-1] ), x=0, y=i*20)
}
# Element i=1 = aardvark
# Element i=2 = apple
# Element i=3 = banana
# Element i=4 = lemon
a = [7, 3.1, 5.1, 4, 9.0, 7]
c = arraySort(a)
for (i=1, ArraySize(c)) {
  SubTitle(Format("Element i={i} = {0} ", c[i-1] ), x=300, y=i*20)
}
# Element i=1 = 3.100000
# Element i=2 = 4
# Element i=3 = 5.100000
# Element i=4 = 7
# Element i=5 = 7
# Element i=6 = 9.000000

General examples

ColorbarsHD()
# array indexes are zero based
a = []
a=ArrayAdd(a,[1,2]) # [[1,2]]
a=ArrayIns(a,3,0) # [3,[1,2]]
a=ArrayAdd(a,"s1") # [3,[1,2],"s1"]
a=ArrayAdd(a,"s2") # [3,[1,2],"s1","s2"]
a=ArrayDel(a,2) # [3,[1,2],"s2"]
a=ArraySet(a,"g",1,0) # [3,["g",2],"s2"]
a=ArrayAdd(a,"h",1) # [3,["g",2,"h"],"s2"]
a=ArrayAdd(a,[10,11,12],1) # append to (1) -> [3,["g",2,"h",[10,11,12]],"s2"]
a=ArrayDel(a,1,3,0) # del from (1,3,0) -> [3,["g",2,"h",[11,12]],"s2"]
a=ArrayAdd(a,"added") # [3,["g",2,"h",[11,12]],"s2","added"]
a=ArrayAdd(a,["yet","another","sub"]) # [3,["g",2,"h",[11,12]],"s2","added",["yet","another","sub"]]
x=a[0] #3
x=a[1,0] #g
x=a[1,2] #h
x=a[1,3,1] #12
x=a[3] #"added"
x=a[4,1] #"another"
SubTitle("x = " + String(x) + " Size=" + String(a.ArraySize()))
array_variable = [[1, 2, 3], [4, 5, 8], "hello"]
dictionary = [["one", 1], ["two", 2]]
empty = []
subarray = array_variable[0]
val = subarray[2]
val2 = array_variable[1, 3]
str = array_variable[2]
n = ArraySize(array_variable) #3
n2 = ArraySize(empty) #0
val3 = dictionary["two"]
a = []
a=ArrayAdd(a,[1,2]) # [[1,2]]
a=ArrayIns(a,3,0) # [3,[1,2]]
a=ArrayAdd(a,"s1") # [3,[1,2],"s1"]
a=ArrayAdd(a,"s2") # [3,[1,2],"s1","s2"]
a=ArrayDel(a,2) # [3,[1,2],"s2"]

b = ["hello", "leo"]
ColorBars()
clip=last
a = [[1,2],[3,4]]
aa = [1]
b = a[1,1] + ArrayGet(a, 1,0) + aa[0]

empty_array = []
empty_array_2 = empty_array
#n3 = empty_array_2.ArrayGet(0) # array index out out range error!

black_yuv_16 = [0,32768,32768]
grey_yuv_16 = [32768,32768,32768]
white_yuv_16 = [65535,32768,32768]
aSelectColors = [\
  ["black", black_yuv_16],\
  ["grey", grey_yuv_16],\
  ["white",white_yuv_16],\
  ["empty",empty_array]\
]
test_array = [99, 1.0, "this is a string"] # mixed types
test_array2 = [199, 2.0, "This is a string"]

n = ArraySize(test_array) # 3
n2 = ArraySize(empty_array_2) # 0
sum = FirstNSum(grey_yuv_16,2)
b = b

clip = clip.Text(e"Array size = " + String(n) +\
 e"\n Empty array size = " + String(n2) +\
 e"\n sum = " + String(sum) +\
 e"\n b = " + String(b) +\
 e"\n white_yuv_16[1]=" + String(aSelectColors["white"][1]) + \
 e"\n [0]=" + String(ArrayGet(test_array,0)) + \
 e"\n [1]=" + String(ArrayGet(test_array,1)) + \
 e"\n [2]=" + ArrayGet(test_array,2), lsp=0, bold=true, font="info_h")

return clip

function FirstNSum(array x, int n)
{
  a = 0
  for (i=0, x.ArraySize()-1) {
    a = a + x[i]
  }
  return a
}

Arrays in user defined functions

Avisynth accepts arrays in the place of "val" script function parameter type regardless of being named or unnamed. (Note: "val" is translateed to "." in internal function signatures)

Example:

BlankClip(pixel_type="yv12")
r([1, 2, 3])
r(n=[10,11,[12,13]])
r("hello")
function r(clip c, val "n")
{
  if (IsArray(n)) {
   if (IsArray(n[2])) {
     return Subtitle(c, String(n[2,1]), align=8) #13 at the top
   } else {
     return Subtitle(c, String(n[2]), align=2) #3 at the bottom
   }
  } else {
    return Subtitle(c, String(n), align=5) #hello in the center
  }
}

Some facts which are inherited from the compatible Avisynth functionality.

  • Array-typed parameters with "name" have the value "Undefined" when they are not passed.

  • But the value is "Defined" and its value is a zero-sized array if the parameter is unnamed, like in other Avisynth functions.

"Array of Anything" issues

What about parameter handling for "array of anything" parameter when array(s) would be passed directly.

Avisynth traditionally makes difference between zero-or-more and one-or-more kind of array parameters. The special case is "array of anything"

  • Avisynth signature: .* or .+

  • Script function specifier val_array or val_array_nz (nz denotes to nonzero)

When parameter signature is array of anything (.+ or .*) and the parameter is passed unnamed (even if it is a named parameter) then there is an ambiguos situation.

Example:

1,2,3 will be detected as [1,2,3] (compatibility: Avisynth collects arrays from comma separated function values, when such function signature is found)

1 will be detected as [1] (compatibility)

(nothing) will be detected as [], but marked in order to override it later directly by name

Consequences:

Passing a direct script array [1,2,3] will be detected as [[1,2,3]], because unnamed and untyped parameters are put together into an array, which has the size (number of elements) of the list. This is a list of 1 element which happens to be an array. Avisynth cannot 'guess' whether we want to define a single array directly or this array is the only one part of the list. [1,2,3] or [ [1,2,3] ]

Syntax hint:

When someone would like to pass a directly specified array (e.g. [1,2,3] instead of 1,2,3) to a .+ or .* parameter the parameter must be passed by name! Or better: instead of "array of anything" use the val (function signature ".") type. It will acceopt any type, including arrays. Then you can check inside your function with IsArray() and ArraySize() if it is really an array.

Because of the existing AviSynth syntax rule: arguments given as unnamed in the place of an array-of-anything parameter are considered to be list elements from which Avisynth creates an array

function foo(val_array "n")
  Call                          n
  foo()                   O.K.  Undefined
  foo(1)                  O.K.  [1] (compatible Avisynth way)
  foo(1,2,3)              O.K.  [1,2,3] (compatible Avisynth way)
  foo([1,2,3])            !     [[1,2,3]] (compatible Avisynth way)
  foo([1,2,3],[4,5])      !     [[1,2,3],[4,5]] (compatible Avisynth way)
  foo(n=[1,2,3])          O.K.  [1,2,3]
  foo(n=[[1,2,3],[4,5]])  O.K.  [[1,2,3],[4,5]]
  foo(n=[])               O.K.  []
  foo(n="hello")          Syntax error, "hello" is not an array

  // unnamed signature
function foo(val_array n)
  Call                          n
  foo()                   O.K.  [] (defined and array size is zero) Avisynth compatible behaviour

Script functions supports avisynth function array - signature '+' (one or more) with _nz type suffix. E.g. int_array_nz - signature '*' (zero or more) without _nz type suffix. E.g. float_array

E.g.: val_array -> .* val_array_nz -> .+, int_array -> i* int_array_nz -> i+ Each basic type has its array and array_nz variant. Such as bool_array_nz, float_array_nz, string_array_nz, clip_array_nz, func_array_nz.

Note 1: There is an error message when a script array is passed to a non-array named function argument (e.g. foo(sigma=[1.1,1.1]) to [foo]f parameter signature

Note 2: Type-free unnamed arrays ".+" or ".*" cannot be followed by additional parameters

Note 3: A backward compatible way (AVS 2.6 and non-script-array AviSynth+ versions) of using named or unnamed arrays is to specify a single type as "." and the plugin would check the argument type by IsArray

User defined functions get array parameter types:

  • "array" or "val_array": array of any type.

    When unnamed, then this kind of parameter must be the very last one. Unnamed free-typed parametes cannot be followed by any other parameter. Translates to ".*" in a plugin parameter definition rule.

  • "bool_array" "int_array", "float_array", "string_array", "clip_array", "func_array"

    Translates to "b*", "i*", "f*", "s*", "c*", "f*" in a plugin parameter definition rule.

  • "bool_array_nz" "int_array_nz", "float_array_nz", "string_array_nz", "clip_array_nz", "func_array_nz"

    Translates to "b+", "i+", "f+", "s+", "c+", "n+" in a plugin parameter definition rule.

Example:

a = [1.0, 2.0, 4.2]
b = [3, 4, 5]
multi = [a,b]

sum = Summa(multi[0], multi[1], 2)
SubTitle(Format({sum}))

Function Summa(array "x", array "y", int "N")
{
  sum = 0.0
  FOR(i=0,N-1) {
    sum = sum + x[i] * y[i]
  }
  return sum
}

or

Function Summa(float_array x, float_array y, int "N")
{
  sum = 0.0
  FOR(i=0,N-1) {
    sum = sum + x[i] * y[i]
  }
  return sum
}

Arrays (pre AviSynth+: AVSLib)

Before Avisynth+ 3.6 arrays were not supported natively by the scripting language.

However, a library named [AVSLib] exists that provides a functional interface for creating and manipulating arrays. Coupled with Avisynth's OOP style for calling functions, one can treat arrays as objects with methods, which is a familiar and easy to understand and code scripting concept.

Therefore, two preparatory steps are needed before being able to create and manipulate process arrays into your script:

  • [Download] and install the most current version of AVSLib into your system.

  • Import the needed AVSLib files in your script as follows (see the instructions inside the library's documentation to fill-in the gaps):

  • AVSLib 1.1.x versions: Enter LoadPackage("avslib", "array") to load the array implementation files, or LoadLibrary("avslib", CONFIG_AVSLIB_FULL) to load entire AVSLib.

  • AVSLib 1.0.x versions: Enter an appropriate Import ({path to AVSLib header}) statement as the first line of your script.

Now you are ready to create your first array! In order to provide an almost real case example let's assume the following (which are commonplace in many situations) about the script you want to create:

  • The script selects a distinct range of frames from each video clip.

  • Some of the input clips may have different size, fps, audio and/or colorspace; thus they need to be converted.

  • Some of the filtering parameters are distinct for each clip.

Having done that, let's proceed to the actual code:

First, we create the array; ..1.., ..2.., etc. are actual filename strings. Clip loading is made by AviSource in the example but DirectShowSource may also be specified.

inp = ArrayCreate( \
    AviSource(..1..), \
    AviSource(..2..), \
    ... \
    AviSource(..n..) )

Then we convert to same fps, audio, colorspace and size by using AssumeFPS, ConvertAudioTo16bit, ConvertToYV12 and BilinearResize respectively (or any resizer that you find fit). We use OOP + chaining to make compact expressions.

Note that since Avisynth does not provide a way for in-place variable modification we must reassign to an array variable after each array operation (usually the same).

inp = inp.ArrayOpFunc("AssumeFPS", "24").ArrayOpFunc("ConvertAudioTo16bit" \
    ).ArrayOpFunc("ConvertToYV12").ArrayOpFunc("BilinearResize", "640,480")

To perform trimming we will use arrays of other types also. Below ts stands for first frame to trim, te for last; each number corresponds to a clip in inp variable.

ts = ArrayCreate(12, 24, ..., 33) # n numbers in total
te = ArrayCreate(8540, 7834, ..., 5712) # n numbers in total

We also need a counter to make things easier; we will use ArrayRange to create an array of 0,1,2,...

cnt = ArrayRange(0, inp.ArrayLen()-1)

In addition we must define a user function that will accept inp, ts, te and cnt and do the trimming.

Since ArrayOpArrayFunc only accepts two arrays for per-element processing, it is easier to pass 'inp' and cnt as array elements and ts, te as entire arrays.

Function MyTrim(clip c, int count, string fs, string fe) {
    return c.Trim(fs.ArrayGet(count), fe.ArrayGet(count))
}

Now we are ready to do the trim (line below).

inp = ArrayOpArrayFunc(inp, cnt, "MyTrim", StrQuote(ts)+","+StrQuote(te))

We will finish the processing with a final tweak on brightness with different settings on each clip and on hue with same settings for all clips.

bright = ArrayCreate(2.0, 1.5, ..., 3.1) # n numbers in total

Function MyTweak(clip c, float br) {
    return c.Tweak(bright=br, hue=12.3)
}

inp = ArrayOpArrayFunc(inp, bright, "MyTweak")

And now we are ready to combine the results and return them as script's output. We will use Dissolve for a smoother transition.

return inp.ArraySum(sum_func="Dissolve", sum_args="5")

This is it; the n input clips have been converted to a common video and audio format, trimmed and tweaked with individual settings and returned as a single video stream with only 11 lines of code (excluding comments).

Other types of array processing are also possible (slicing ie operation on a subset of elements, joining, multiplexing, etc.) but these are topics to be discussed in other pages. Those that are interested can browse the AVSLib documentation. One can also take a closer look at the examples section of the AVSLib documentation.


Back to scripting reference.

$Date: 2024/01/11 14:40:00 $