Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > Capturing and Editing Video > Avisynth Usage

Reply
 
Thread Tools Search this Thread Display Modes
Old 5th February 2006, 14:43   #1  |  Link
Richard Berg
developer wannabe
 
Richard Berg's Avatar
 
Join Date: Nov 2001
Location: Brooklyn, NY
Posts: 1,211
Arrays and Loops

I was writing a helper function yesterday and once again came across a situation where it would've been nice to have arrays & loops. After many years of people asking for these kind of features, but no progress aside from "someday, in AVS 3.xx", I assumed it was a hard problem. Turns out it only took a few hours

Code:
# rb-array.avsi -- functions for simulating arrays & loops in Avisynth 2.xx
#
# Last modified: 2006-02-05
#
# Written by Richard Berg and assigned to the public domain.
#


# Dim
#
# 	Create a collection of global variables that can be manipulated similar to an array.  You can re-Dim
#   an existing array, but the contents will be cleared.  The type of the "initValue" parameter determines the type
#	of the array.  All Avisynth types are supported.
#
# PARAMETERS:
#			"arr" 		:	Name of the array variable.  Pass this variable name to Deref and Set.
#			"size"		:	Size of the array; 0-based, like C/C++.  Dim("foo", 10) creates foo0-foo9.
#			"initValue"	:	Each element is initialized to this value.  Its type determines the array type.  It uses For
#							internally, so initValue can be parametrized by 'i'.
#
# USAGE: see example.avs
#
function Dim(string arr, int size, val initValue)
{
	# create "base" string variable whose value is its name.  every Eval in internal array functions 
	# needs this to build the element's name
	Eval("global " + arr + "=" + chr(34) + arr + chr(34))
	
	# store size so that Length() is O(1)
	Eval("global " + arr + "Length = " + string(size))
	
	# create each element
	global DimThunkVal = initValue   # thunk requires this in case it's a clip
	for(0, "<="+string(size), "+1", arr + ".set(i, " + thunk(initValue, "DimThunkVal") + ")")
}


# DeRef
#
#	Gives you programmatic access to the "arrays" created by Dim
#
# USAGE: foo.deref(2+2)
#				...is semantically equivalent* to foo[2+2] in C/C++
#				...is syntactically equivalent to foo4 in Avisynth
#
# 				*exception: the output of deref is NOT an lvalue.  If you need to assign something to 
#							an array element, use Set.
#
function DeRef(string arr, int index)
{
	Eval(arr + string(index))
}


# Set
#
#	Allows you to programmatically assign values to the "arrays" created by Dim
#
# USAGE: foo.set(i, i*2)
#				...is semantically equivalent to foo[i] = i*2; in C/C++
#				...is syntactically equivalent to fooI = i*2 in Avisynth
#
function Set(string arr, int index, val val)
{
	Eval("global " + arr + string(index) + "=" + thunk(val))
}


# Thunk
#
#	Makes a generic (untyped) parameter suitable for building Eval strings.  
#
# PARAMETERS:
#		"clipName"	:	In order to assign clips to arrays with Eval, the rvalue has to be another Eval
#						on a clip variable.  By default, this variable is the parameter "val," as used
#						in functions like Set.  However, if you are building an Eval string for use with
#						For, you'll need to set a global variable that can be accessed inside the loop.  So
#						as to not pollute the global namespace, you can create a unique variable name; pass
#						it as 'clipName' to make Thunk work correctly.
#						
#
function Thunk(val val, string "clipName")
{
	clipName = Default(clipName, "val")

	# string -> escape it
	# clip -> double-thunk it
	# everything else -> stringify it
	return val.IsString()
		\ ? chr(34) + val + chr(34)		
		\ : val.IsClip()
			\ ? "Eval(" + chr(34) + clipName + chr(34) + ")"
			\ : string(val)
}


# Length
#
#	Returns the length of an "array" created by Dim
#
function Length(string arr)
{	
	try {
		return Eval(arr + "Length")
	}
	catch (err_msg)	{
		Throw("Length: the array " + arr + " was not created by Dim or is corrupt")
	}	
}


# Length2
#
#	Returns the length of an "array" that was not created by Dim, or has had elements manipulated directly.  O(n) search.
#	
# PARAMETERS:
#		"startIndex"	:	Start searching at this index.  Default = 0.  Note: elements lower than startIndex are
#							not counted in the length calculation.
#
function Length2(string arr, int "startIndex")
{
	startIndex = Default(startIndex, 0)
	
	# store size in Dim-style global so we don't have to do this again
	Eval("global " + arr + "Length = " + string(Length2Loop(arr, startIndex)))
	return arr.length
}

function Length2Loop(string arr, int index)
{
	try {
		# note: using deref won't work - after the callstack unwinds, index will be lost
		Eval(arr + string(index))
		return Length2Loop(arr, index + 1)
	}
	catch (err_msg) {
		return index
	}
}


# Fill
#
#	Fill an array with values entered as a comma-delimited string literal. 
#	(thanks to James "stickboy" Lin for the idea)
#	
#
# USAGE: array.Fill("3, -50, 124")
#	
# PARAMETERS:
#		"index" 	:	Start writing values at this index.  Default is 0.
#		"string"	:	If true, fills the array with strings instead of parsing into values.  Default is false.
#
function Fill(string arr, string values, int "index", bool "string")
{
	index = Default(index, 0)
	string = Default(string, false)
	
	offset = FindStr(values, ",")
	# FindStr returns 0 if not found
	return (offset == 0)
		\ ? NOP
		\ : Eval("""
				element = LeftStr(values, offset - 1)
				element = string ? element : Value(element)
				arr.Set(index, element)
				arr.Fill(MidStr(values, offset + 1), index + 1)
			""")			
}


# For
#
# 	A simple for-loop construct for Avisynth 2.xx
#
# USAGE: for(1, "<=3", "+1", "global x = x*2" )
#				...is semantically equivalent to for(int i=1, i<=3, i=i+1) { x = x*2 } in C/C++
#
# Notes:
#	- 	with For, the loop variable is always 'i', and the first parameter (its initial value) is always an int.  If you 
#		want to use a custom loop variable (so you can nest loops, for instance, or have a floating-point counter) then use For2.
#	-	any script variables you want manipulate inside the loop must be globals
#	-	corollary: you cannot use the implicit 'last' variable.  if you want to apply a filter inside the loop,
#		you must use assignment syntax.  Example: for(0, "<=2", "+1", "global clip = clip.FFT3DFilter(sigma=2.5, plane=i)" )
#
function For(int i, string cond, string iter, string block)
{
	return Eval("i" + cond)
		\ ? Eval("""
				Eval(block)
				For(Eval("i" + iter), cond, iter, block)
			""")
		\ : NOP
}


# For2
#
# 	A more generalized for-loop construct for Avisynth 2.xx
#
# USAGE: for("j=1", "j<=3", "j=j+1", "global x = x*2")
#				...is semantically equivalent to for(int j=1, j<=3, j=j+1) { x = x*2 } in C/C++
#
# Notes: 
#	-	For's guidelines about manipulating globals apply to For2 as well.  In addition, the loop variable must 
#		be a global (this is handled for you -- if this hack isn't sufficiently general, use For2Loop directly).
#
function For2(string init, string cond, string iter, string block)
{
	For2Loop("global " + init, cond, "global " + iter, block)
}

function For2Loop(string init, string cond, string iter, string block)
{
	Eval(init)
	return Eval(cond)
		\ ? Eval("""
					Eval(block)
					For2Loop(iter, cond, iter, block)
				""")
		\ : NOP
}

# For3
#
#	Same as For, but implemented using For2.  Mostly a proof of concept, but there's a practical difference: the 
#	loop variable here is global, so nesting For3's probably does weird things.
#
function For3(int i, string cond, string iter, string block)
{
	For2("i=" + string(i), "i" + cond, "i=i" + iter, block)
}


# While
#
#	A simple while-loop construct for Avisynth 2.xx
#
# USAGE: while(b, "DoSomething()")
#				...is semantically equivalent to while(b) { DoSomething() } in C/C++
#
# Notes:
#	-	If you put it into an infinite loop, the host app will crash after several seconds.  An easy way to do this
#		is to forget to use 'global' when manipulating variables that affect 'cond' inside the loop.
#	- 	I honestly don't think this is a very useful function.  Most of the time you want while()-like semantics, you
#		really want to operate on frames (not clips or other variables), in which case ConditionalFilter is the far 
#		better choice.
#
function While(string cond, string block)
{
	For2Loop("", cond, "", block)
}


A simple example.avs:
Code:
BlankClip(length=1025, color=color_royalblue)
JDL_FadeIO(end=last.framecount)
global fade = last

# create an array full of 0's, then fill it with the fibonacci sequence
Dim("fibArr", 25, 0)
for(0, "<=25", "+1", 
\"""	
	global fade = fade.Subtitle("initial value #" + string(i) + " = " + string(fibArr.deref(i)), y = (i+1) * 18, align = 6)
	fibArr.set(i, i>=2 
		\ ? fibArr.deref(i-1) + fibArr.deref(i-2)
		\ : 1)
	global fade = fade.Subtitle("Fibonacci #" + string(i) + " = " + string(fibArr.deref(i)), y = i * 18)	
""")

# write a multiplication table
global table = fade
for2("x=1", "x<=8", "x=x+1",
\"""
	for2("y=1", "y<=8", "y=y+1",
	\"
		global table = table.Subtitle(string(x*y), x=x*30 + 180, y=y*20 + 30)
	")
""")
	

	
# create an array of clips: each has 1 frame that's a power of 2 in the original clip
global j=0
Dim("frameArr", 0, NullClip(fade))  # create empty array; we'll fill it manually
for(1, "<=table.FrameCount", "*2",
\"""	
	frameArr.set(
		\ j, table.Trim3(i, length=1)
		\ .Subtitle("original frame " + string(i), align=8)
		\ .Subtitle("fibArr length " + string(fibArr.length), align=5)
	\)
	global j = j + 1			
""")

# concatenate the frames from that clip-array into one clip
# we'll do it backwards just for fun
global powers = NullClip(fade)
len = Length("frameArr")  # change this to a bogus array name and you'll get an Assert
global countdown = frameArr.Length2 - 1
while("countdown>=0", 
\"""
	global powers = powers + frameArr.deref(countdown).subtitle("powers frame " + string(countdown), align=8, y = 18)
	global countdown = countdown-1
""")

# detecting the length of frameArr manually (see Length2 call above) is O(N) the first time, so we only want to 
# do it once; afterward, Length() will work
len2 = frameArr.Length
return powers.Subtitle("frameArr len: before " + string(len) + ", after " + string(len2), align = 2)


FWIW, the function that started it all (FindClosest):
Code:
# rb-alignaudio.avsi -- AlignAudio & misc helper functions
#
# Last modified: 2006-02-05
#
# Written by Richard Berg and assigned to the public domain.
#


# AlignAudio
#
#   Adjusts the length of the audio track to match the video.  Uses BestResample if resampling is requested.
#
#
#
# PARAMETERS:
#   "samplerate" : 	If defined, resample the audio to this rate.  If not defined, then the audio samplerate is 
# 					set to the standard that's closest to the input.
# 
Function AlignAudio(clip clip, int "samplerate")
{
	Assert(clip.HasAudio && clip.HasVideo, "RB_AlignAudio: clip must have both audio & video")
	
    clip = Defined(samplerate)
    	\ ? clip.BestResample(samplerate)
    	\ : clip.AssumeSampleRate( FindClosest(clip.AudioRate, "11025, 22050, 44100, 88200, 12000, 24000, 48000, 96000, 192000") )
    
    return clip.TimeStretch(100. * clip.AudioLength/clip.AudioRate / (clip.FrameCount/clip.FrameRate))
}



# BestResample
# 	
#	Resamples a clip's audio.  Intelligently chooses SSRC(fast = true), SSRC(fast = false), or ResampleAudio based on 
#	the guidelines in the Avisynth manual.
#
#
function BestResample(clip clip, int samplerate)
{
	return CanSSRC(clip.AudioRate, samplerate) 
    		\ ? WithinFactorOf(2, clip.AudioRate, samplerate)
    			\ ? clip.SSRC(samplerate)
    			\ : clip.SSRC(samplerate, fast=false)
    		\ : clip.ResampleAudio(samplerate)
}    		



# Given a list of numbers, returns the one closest to the input
#
# PARAMETERS:
#		"input" 	:	number to compare against
#		"numbers"	:	comma-delimited list of numbers to search
#
#
function FindClosest(val input, string numbers)
{
	# parse string into array
	Dim("rates", 0, 0) 	# empty is ok - we'll fill it manually
	Fill(rates, numbers)
	
	# need Length2 here since we filled manually
	global g_len = rates.Length2 
	global g_minDelta = input - rates0
	global g_input = input
	for(1, "<g_len", "+1", 
	\"""
		global g_minDelta = abs(g_input - rates.deref(i)) < abs(g_minDelta)
			\ ? g_input - rates.deref(i)
			\ : g_minDelta
	""")
		
	ret = input - g_minDelta
	return input.IsInt ? int(ret) : ret
}



# return true if f is less than the ratio between x & y
# 	Example: WithinFactorOf(10, x, y) == "x is within an order of magnitude of y"
#
function WithinFactorOf(float f, float x, float y)
{
	ratio = x>y ? x/y : y/x
	return ratio < f
}


# Computes the Greatest Common Divisor of two integers
#   (Euclid's Algorithm)
#
function GCD(int a, int b)
{
	return (b == 0) 
   		\ ? a
   		\ : GCD(b, a % b)
}


# Returns true if Avisynth's Shibatch sample rate converter function (SSRC) can convert
# 	a clip with samplerate = src to dst, based on the documentation at <http://www.avisynth.org/SSRC>
#
function CanSSRC(int src, int dst)
{
	fs1 = dst * src / GCD(src, dst)
	return (fs1 / dst == 1) || (fs1 / dst % 2 == 0) || (fs1 / dst % 3 == 0)
}

Try it out, let me know what you think. I'm sure there are more features that can be added without much trouble, but I dunno what would be useful.
Richard Berg is offline   Reply With Quote
Old 5th February 2006, 14:56   #2  |  Link
Wilbert
Moderator
 
Join Date: Nov 2001
Location: Netherlands
Posts: 6,364
arrays are supported in AVSLib: http://avslib.sourceforge.net/modules/array.html

I will add the stuff above to the documentation, if you haven't already done so
Wilbert is offline   Reply With Quote
Old 5th February 2006, 15:32   #3  |  Link
Richard Berg
developer wannabe
 
Richard Berg's Avatar
 
Join Date: Nov 2001
Location: Brooklyn, NY
Posts: 1,211
Interesting...surprised I never saw AVSLib before. (Maybe I did & forgot). Anyway, my fake arrays should be much faster than their fake arrays & won't have the 512-byte limit. They also don't have any loops Nevertheless, they have a huge collection of array operations that would be nice to have...I'll bet there's an easy way to adapt them, but I'm tired of scripting for now...
Richard Berg is offline   Reply With Quote
Old 6th February 2006, 16:34   #4  |  Link
niiyan
Registered User
 
Join Date: Sep 2002
Posts: 88
WScript and WSInvoke allow you to use Windows Script (VBScript, JScript and so on) in AviSynth.
These filters are included in a warpsharp plugin.
niiyan is offline   Reply With Quote
Old 19th February 2006, 13:56   #5  |  Link
Richard Berg
developer wannabe
 
Richard Berg's Avatar
 
Join Date: Nov 2001
Location: Brooklyn, NY
Posts: 1,211
Dunno if anyone actually tried my example.avs, but here's something for scriptmeister Didee. From MCNR_simple2, we simplify:

Code:
  bw4 = (frames<4) ? dummy : clp.SrchCmpRp(blocksize,true, 4,chroME,repairME)
  bw3 = (frames<3) ? dummy : clp.SrchCmpRp(blocksize,true, 3,chroME,repairME)
  bw2 = (frames<2) ? dummy : clp.SrchCmpRp(blocksize,true, 2,chroME,repairME)
  bw1 =                      clp.SrchCmpRp(blocksize,true, 1,chroME,repairME)
  fw1 =                      clp.SrchCmpRp(blocksize,false,1,chroME,repairME)
  fw2 = (frames<2) ? dummy : clp.SrchCmpRp(blocksize,false,2,chroME,repairME)
  fw3 = (frames<3) ? dummy : clp.SrchCmpRp(blocksize,false,3,chroME,repairME)
  fw4 = (frames<4) ? dummy : clp.SrchCmpRp(blocksize,false,4,chroME,repairME)
  
  frames == 1  ?  interleave(             bw1,clp,fw1             )  :  \
  frames == 2  ?  interleave(         bw2,bw1,clp,fw1,fw2         )  :  \
  frames == 3  ?  interleave(     bw3,bw2,bw1,clp,fw1,fw2,fw3     )  :  \
                  interleave( bw4,bw3,bw2,bw1,clp,fw1,fw2,fw3,fw4 )
...becomes...
Code:
  Dim("bw", frames, NullClip())
  Dim("fw", frames , NullClip())
  global inter = "clp"
  for(1, "<=" + string(frames), "+1",
  \"""
      bw.set(i, clp.SrchCmpRp(blocksize,true,i,chroME,repairME))
      fw.set(i, clp.SrchCmpRp(blocksize,true,i,chroME,repairME))  
      global inter = "bw" + string(i) + "," + inter + "," + "fw" + string(i)
  """)  
   eval( "interleave(" + inter + ")" )
...and in the process, is able to handle the 'frames' parameter without any limit.
Richard Berg is offline   Reply With Quote
Old 18th May 2006, 00:20   #6  |  Link
redfordxx
Registered User
 
Join Date: Jan 2005
Location: Praha (not that one in Texas)
Posts: 863
Hi Richard,
thanks for these functions. I show you part of my to-be script:
Code:
function SmoothDeblockLayer(clip orig,string "dct_type",int "density",float "quant",string "averagingmatrix")
{
dct_type="DCTFilter"    		#fixed now
global gDensity=density  		#number of DCT per 8 pix 
global gOrig=orig
global rep=1                            #rounding error protection
global sc="shiftClip"
global avg=sc+"0,"+String(rep)
global coma=","
global mx=orig.PtrnBlock(3,0,0,255,255) # needed Patterns.avsi (from redeblock thread)
global _lut1="x y * 255 /"

Dim("DctParams",8,0)
Dim("shiftClip",Int(Pow(density,2)),Blackness())

For2("i=7","i>="+String(quant),"i=i-1",
\"""
    DctParams.Set(i,1)
""")

For2("i=0", "i<gDensity", "i=i+1",
\"""
    For2("j=0", "j<gDensity", "j=j+1",
    \"
	shiftClip.Set(i*gDensity+j,(i+j==0) ? gOrig : gOrig.AddBorders(Int(8/gDensity)*i,Int(8/gDensity)*j,16-Int(8/gDensity)*i,16-Int(8/gDensity)*j))
	shiftClip.Set(i*gDensity+j,(i+j==0) ? gOrig : shiftClip.DeRef(i*gDensity+j).DCTFilter(DctParams7,DctParams6,DctParams5,DctParams4,DctParams3,DctParams2,DctParams1,DctParams0))
	shiftClip.Set(i*gDensity+j,mt_lutxy(shiftClip.DeRef(i*gDensity+j),mx,_lut1,y=3,u=3,v=3))
	shiftClip.Set(i*gDensity+j,(i+j==0) ? shiftClip.DeRef(i*gDensity+j) : shiftClip.DeRef(i*gDensity+j).crop(Int(8/gDensity)*i,Int(8/gDensity)*j,Int(8/gDensity)*i-16,Int(8/gDensity)*j-16))
	global avg = (i+j==0) ? avg : avg+coma+sc+String(i*gDensity+j)+coma+String(rep)
    ")
""")
Eval("Average("+avg+")")
}
Can you pls tell me
- is it possible to get rid of these artificially added globals in the beginning of the function?
- you see that I used variable sc inside the inner loop to pass string because I was not sure how to write qoutation marks inside qoutation marks inside qoutation marks ...
- anything else I could do better with your scripts

Last edited by redfordxx; 18th May 2006 at 02:02.
redfordxx is offline   Reply With Quote
Old 12th August 2017, 17:11   #7  |  Link
bcn_246
Registered User
 
bcn_246's Avatar
 
Join Date: Nov 2005
Location: UK
Posts: 117
Not sure if the author is still around, but I am getting a "expected a variable name" error when trying to load the script (using AviSynth+ r2508 x86).

bcn_246 is offline   Reply With Quote
Old 12th August 2017, 17:41   #8  |  Link
feisty2
I'm Siri
 
feisty2's Avatar
 
Join Date: Oct 2012
Location: void
Posts: 2,633
Seriously...?
It was last updated in 2006 and u expect it to work??
feisty2 is offline   Reply With Quote
Old 12th August 2017, 17:53   #9  |  Link
feisty2
I'm Siri
 
feisty2's Avatar
 
Join Date: Oct 2012
Location: void
Posts: 2,633
Oh, forgot to do some advertizing
Give vaporsynth a shot if u want complex stuff like arrays or loops or conditional statements, it's got Python as the scripting language so u have all that natively with no extra trouble in vaporsynth
feisty2 is offline   Reply With Quote
Old 13th August 2017, 03:52   #10  |  Link
real.finder
Registered User
 
Join Date: Jan 2012
Location: Mesopotamia
Posts: 2,587
Quote:
Originally Posted by bcn_246 View Post
Not sure if the author is still around, but I am getting a "expected a variable name" error when trying to load the script (using AviSynth+ r2508 x86).

try this edit

Code:
# rb-array.avsi -- functions for simulating arrays & loops in Avisynth 2.xx
#
# Last modified: 2017-08-13
#
# Written by Richard Berg and assigned to the public domain.
#


# RBDim
#
# 	Create a collection of global variables that can be manipulated similar to an array.  You can re-Dim
#   an existing array, but the contents will be cleared.  The type of the "initValue" parameter determines the type
#	of the array.  All Avisynth types are supported.
#
# PARAMETERS:
#			"arr" 		:	Name of the array variable.  Pass this variable name to Deref and Set.
#			"size"		:	Size of the array; 0-based, like C/C++.  RBDim("foo", 10) creates foo0-foo9.
#			"initValue"	:	Each element is initialized to this value.  Its type determines the array type.  It uses For
#							internally, so initValue can be parametrized by 'i'.
#
# USAGE: see example.avs
#
function RBDim(string arr, int size, val initValue)
{
	# create "base" string variable whose value is its name.  every Eval in internal array functions 
	# needs this to build the element's name
	Eval("global " + arr + "=" + chr(34) + arr + chr(34))
	
	# store size so that RBLength() is O(1)
	Eval("global " + arr + "RBLength = " + string(size))
	
	# create each element
	global DimThunkVal = initValue   # thunk requires this in case it's a clip
	RBFor(0, "<="+string(size), "+1", arr + ".RBSet(i, " + RBThunk(initValue, "DimThunkVal") + ")")
}


# DeRef
#
#	Gives you programmatic access to the "arrays" created by Dim
#
# USAGE: foo.DeRef(2+2)
#				...is semantically equivalent* to foo[2+2] in C/C++
#				...is syntactically equivalent to foo4 in Avisynth
#
# 				*exception: the output of DeRef is NOT an lvalue.  If you need to assign something to 
#							an array element, use RBSet.
#
function DeRef(string arr, int index)
{
	Eval(arr + string(index))
}


# RBSet
#
#	Allows you to programmatically assign values to the "arrays" created by Dim
#
# USAGE: foo.RBSet(i, i*2)
#				...is semantically equivalent to foo[i] = i*2; in C/C++
#				...is syntactically equivalent to fooI = i*2 in Avisynth
#
function RBSet(string arr, int index, val val)
{
	Eval("global " + arr + string(index) + "=" + RBThunk(val))
}


# RBThunk
#
#	Makes a generic (untyped) parameter suitable for building Eval strings.  
#
# PARAMETERS:
#		"clipName"	:	In order to assign clips to arrays with Eval, the rvalue has to be another Eval
#						on a clip variable.  By default, this variable is the parameter "val," as used
#						in functions like RBSet.  However, if you are building an Eval string for use with
#						For, you'll need to RBSet a global variable that can be accessed inside the loop.  So
#						as to not pollute the global namespace, you can create a unique variable name; pass
#						it as 'clipName' to make RBThunk work correctly.
#						
#
function RBThunk(val val, string "clipName")
{
	clipName = Default(clipName, "val")

	# string -> escape it
	# clip -> double-thunk it
	# everything else -> stringify it
	return val.IsString()
		\ ? chr(34) + val + chr(34)		
		\ : val.IsClip()
			\ ? "Eval(" + chr(34) + clipName + chr(34) + ")"
			\ : string(val)
}


# RBLength
#
#	Returns the length of an "array" created by Dim
#
function RBLength(string arr)
{	
	try {
		return Eval(arr + "RBLength")
	}
	catch (err_msg)	{
		Throw("RBLength: the array " + arr + " was not created by Dim or is corrupt")
	}	
}


# Length2
#
#	Returns the length of an "array" that was not created by Dim, or has had elements manipulated directly.  O(n) search.
#	
# PARAMETERS:
#		"startIndex"	:	Start searching at this index.  Default = 0.  Note: elements lower than startIndex are
#							not counted in the length calculation.
#
function Length2(string arr, int "startIndex")
{
	startIndex = Default(startIndex, 0)
	
	# store size in Dim-style global so we don't have to do this again
	Eval("global " + arr + "RBLength = " + string(Length2Loop(arr, startIndex)))
	return arr.RBLength
}

function Length2Loop(string arr, int index)
{
	try {
		# note: using DeRef won't work - after the callstack unwinds, index will be lost
		Eval(arr + string(index))
		return Length2Loop(arr, index + 1)
	}
	catch (err_msg) {
		return index
	}
}


# RBFill
#
#	Fill an array with values entered as a comma-delimited string literal. 
#	(thanks to James "stickboy" Lin for the idea)
#	
#
# USAGE: array.RBFill("3, -50, 124")
#	
# PARAMETERS:
#		"index" 	:	Start writing values at this index.  Default is 0.
#		"string"	:	If true, fills the array with strings instead of parsing into values.  Default is false.
#
function RBFill(string arr, string values, int "index", bool "string")
{
	index = Default(index, 0)
	string = Default(string, false)
	
	offset = FindStr(values, ",")
	# FindStr returns 0 if not found
	return (offset == 0)
		\ ? NOP
		\ : Eval("""
				element = LeftStr(values, offset - 1)
				element = string ? element : Value(element)
				arr.RBSet(index, element)
				arr.RBFill(MidStr(values, offset + 1), index + 1)
			""")			
}


# RBFor
#
# 	A simple for-loop construct for Avisynth 2.xx
#
# USAGE: RBFor(1, "<=3", "+1", "global x = x*2" )
#				...is semantically equivalent to RBFor(int i=1, i<=3, i=i+1) { x = x*2 } in C/C++
#
# Notes:
#	- 	with RBFor, the loop variable is always 'i', and the first parameter (its initial value) is always an int.  If you 
#		want to use a custom loop variable (so you can nest loops, for instance, or have a floating-point counter) then use For2.
#	-	any script variables you want manipulate inside the loop must be globals
#	-	corollary: you cannot use the implicit 'last' variable.  if you want to apply a filter inside the loop,
#		you must use assignment syntax.  Example: RBFor(0, "<=2", "+1", "global clip = clip.FFT3DFilter(sigma=2.5, plane=i)" )
#
function RBFor(int i, string cond, string iter, string block)
{
	return Eval("i" + cond)
		\ ? Eval("""
				Eval(block)
				RBFor(Eval("i" + iter), cond, iter, block)
			""")
		\ : NOP
}


# For2
#
# 	A more generalized for-loop construct for Avisynth 2.xx
#
# USAGE: for2("j=1", "j<=3", "j=j+1", "global x = x*2")
#				...is semantically equivalent to for2(int j=1, j<=3, j=j+1) { x = x*2 } in C/C++
#
# Notes: 
#	-	For's guidelines about manipulating globals apply to For2 as well.  In addition, the loop variable must 
#		be a global (this is handled for you -- if this hack isn't sufficiently general, use For2Loop directly).
#
function For2(string init, string cond, string iter, string block)
{
	For2Loop("global " + init, cond, "global " + iter, block)
}

function For2Loop(string init, string cond, string iter, string block)
{
	Eval(init)
	return Eval(cond)
		\ ? Eval("""
					Eval(block)
					For2Loop(iter, cond, iter, block)
				""")
		\ : NOP
}

# For3
#
#	Same as For, but implemented using For2.  Mostly a proof of concept, but there's a practical difference: the 
#	loop variable here is global, so nesting For3's probably does weird things.
#
function For3(int i, string cond, string iter, string block)
{
	For2("i=" + string(i), "i" + cond, "i=i" + iter, block)
}


# RBWhile
#
#	A simple while-loop construct for Avisynth 2.xx
#
# USAGE: RBWhile(b, "DoSomething()")
#				...is semantically equivalent to RBWhile(b) { DoSomething() } in C/C++
#
# Notes:
#	-	If you put it into an infinite loop, the host app will crash after several seconds.  An easy way to do this
#		is to forget to use 'global' when manipulating variables that affect 'cond' inside the loop.
#	- 	I honestly don't think this is a very useful function.  Most of the time you want While()-like semantics, you
#		really want to operate on frames (not clips or other variables), in which case ConditionalFilter is the far 
#		better choice.
#
function RBWhile(string cond, string block)
{
	For2Loop("", cond, "", block)
}

example.avs

Code:
BlankClip(length=1025, color=$4169e1)
JDL_FadeIO(end=last.framecount)
global fade = last

# create an array full of 0's, then fill it with the fibonacci sequence
RBDim("fibArr", 25, 0)
RBfor(0, "<=25", "+1", 
\"""	
	global fade = fade.Subtitle("initial value #" + string(i) + " = " + string(fibArr.deref(i)), y = (i+1) * 18, align = 6)
	fibArr.RBset(i, i>=2 
		\ ? fibArr.deref(i-1) + fibArr.deref(i-2)
		\ : 1)
	global fade = fade.Subtitle("Fibonacci #" + string(i) + " = " + string(fibArr.deref(i)), y = i * 18)	
""")

# write a multiplication table
global table = fade
for2("x=1", "x<=8", "x=x+1",
\"""
	for2("y=1", "y<=8", "y=y+1",
	\"
		global table = table.Subtitle(string(x*y), x=x*30 + 180, y=y*20 + 30)
	")
""")
	

	
# create an array of clips: each has 1 frame that's a power of 2 in the original clip
global j=0
RBDim("frameArr", 0, NullClip(fade))  # create empty array; we'll fill it manually
RBfor(1, "<=table.FrameCount", "*2",
\"""	
	frameArr.RBset(
		\ j, table.Trim3(i, length=1)
		\ .Subtitle("original frame " + string(i), align=8)
		\ .Subtitle("fibArr length " + string(fibArr.RBlength), align=5)
	\)
	global j = j + 1			
""")

# concatenate the frames from that clip-array into one clip
# we'll do it backwards just for fun
global powers = NullClip(fade)
len = RBLength("frameArr")  # change this to a bogus array name and you'll get an Assert
global countdown = frameArr.Length2 - 1
RBwhile("countdown>=0", 
\"""
	global powers = powers + frameArr.deref(countdown).subtitle("powers frame " + string(countdown), align=8, y = 18)
	global countdown = countdown-1
""")

# detecting the length of frameArr manually (see Length2 call above) is O(N) the first time, so we only want to 
# do it once; afterward, Length() will work
len2 = frameArr.RBLength
return powers.Subtitle("frameArr len: before " + string(len) + ", after " + string(len2), align = 2)
Code:
# rb-alignaudio.avsi -- AlignAudio & misc helper functions
#
# Last modified: 2017-08-13
#
# Written by Richard Berg and assigned to the public domain.
#


# AlignAudio
#
#   Adjusts the length of the audio track to match the video.  Uses BestResample if resampling is requested.
#
#
#
# PARAMETERS:
#   "samplerate" : 	If defined, resample the audio to this rate.  If not defined, then the audio samplerate is 
# 					set to the standard that's closest to the input.
# 
Function AlignAudio(clip clip, int "samplerate")
{
	Assert(clip.HasAudio && clip.HasVideo, "RB_AlignAudio: clip must have both audio & video")
	
    clip = Defined(samplerate)
    	\ ? clip.BestResample(samplerate)
    	\ : clip.AssumeSampleRate( FindClosest(clip.AudioRate, "11025, 22050, 44100, 88200, 12000, 24000, 48000, 96000, 192000") )
    
    return clip.TimeStretch(100. * clip.AudioLength/clip.AudioRate / (clip.FrameCount/clip.FrameRate))
}



# BestResample
# 	
#	Resamples a clip's audio.  Intelligently chooses SSRC(fast = true), SSRC(fast = false), or ResampleAudio based on 
#	the guidelines in the Avisynth manual.
#
#
function BestResample(clip clip, int samplerate)
{
	return CanSSRC(clip.AudioRate, samplerate) 
    		\ ? WithinFactorOf(2, clip.AudioRate, samplerate)
    			\ ? clip.SSRC(samplerate)
    			\ : clip.SSRC(samplerate, fast=false)
    		\ : clip.ResampleAudio(samplerate)
}    		



# Given a list of numbers, returns the one closest to the input
#
# PARAMETERS:
#		"input" 	:	number to compare against
#		"numbers"	:	comma-delimited list of numbers to search
#
#
function FindClosest(val input, string numbers)
{
	# parse string into array
	RBDim("rates", 0, 0) 	# empty is ok - we'll fill it manually
	RBFill(rates, numbers)
	
	# need Length2 here since we filled manually
	global g_len = rates.Length2 
	global g_minDelta = input - rates0
	global g_input = input
	for(1, "<g_len", "+1", 
	\"""
		global g_minDelta = abs(g_input - rates.deref(i)) < abs(g_minDelta)
			\ ? g_input - rates.deref(i)
			\ : g_minDelta
	""")
		
	ret = input - g_minDelta
	return input.IsInt ? int(ret) : ret
}



# return true if f is less than the ratio between x & y
# 	Example: WithinFactorOf(10, x, y) == "x is within an order of magnitude of y"
#
function WithinFactorOf(float f, float x, float y)
{
	ratio = x>y ? x/y : y/x
	return ratio < f
}


# Computes the Greatest Common Divisor of two integers
#   (Euclid's Algorithm)
#
function GCD(int a, int b)
{
	return (b == 0) 
   		\ ? a
   		\ : GCD(b, a % b)
}


# Returns true if Avisynth's Shibatch sample rate converter function (SSRC) can convert
# 	a clip with samplerate = src to dst, based on the documentation at <http://www.avisynth.org/SSRC>
#
function CanSSRC(int src, int dst)
{
	fs1 = dst * src / GCD(src, dst)
	return (fs1 / dst == 1) || (fs1 / dst % 2 == 0) || (fs1 / dst % 3 == 0)
}
__________________
See My Avisynth Stuff

Last edited by real.finder; 13th August 2017 at 04:42.
real.finder is offline   Reply With Quote
Old 13th August 2017, 06:42   #11  |  Link
raffriff42
Retried Guesser
 
raffriff42's Avatar
 
Join Date: Jun 2012
Posts: 1,373
Quote:
Originally Posted by real.finder View Post
try this edit...
I see you have renamed the functions to avoid a name collision. I agree with that. Avisynth+ reserves for and while which are keywords in GScript, but with different syntax.

RBLength calls Throw("...") which does not exist; replace with Assert(false, "...")

The test script has some oddities too, like JDL_FadeIO (replace with FadeIO(30)) and NullClip (replace with BlankClip)

While the resulting script runs, the output is just a black screen with the message frameArr len: before 0, after 11. It's probably not running correctly.
raffriff42 is offline   Reply With Quote
Old 13th August 2017, 09:44   #12  |  Link
real.finder
Registered User
 
Join Date: Jan 2012
Location: Mesopotamia
Posts: 2,587
Quote:
Originally Posted by raffriff42 View Post
I see you have renamed the functions to avoid a name collision. I agree with that. Avisynth+ reserves for and while which are keywords in GScript, but with different syntax.

RBLength calls Throw("...") which does not exist; replace with Assert(false, "...")

The test script has some oddities too, like JDL_FadeIO (replace with FadeIO(30)) and NullClip (replace with BlankClip)

While the resulting script runs, the output is just a black screen with the message frameArr len: before 0, after 11. It's probably not running correctly.
yes, it need another update, I just did quick edit for bcn_246

maybe later I will did more check for this especially if I need Arrays and Loops in future

and since pinterf care about Arrays and did added it before in avs+ but remove it cuz it causing compatibility problems with 2.5 plugins, maybe this Thread by Richard Berg will give him some ideas
__________________
See My Avisynth Stuff
real.finder is offline   Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 11:32.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.