AviSynth Syntax - Steuerstrukturen

Streng genommen bietet die AviSynth Syntax nur eine einzige Kontrollstruktur (eigentlich zwei, die andere ist der bedingte Operator:?, der an anderer Stelle erklärt wird), das try..catch Statement.

Inhalt

Das try..catch Statement

Das try..catch Statement erlaubt die Ausführung von Code, der eventuell einen Runtime Error hervorrufen kann und den Umgang mit dem Fehler, falls er eintritt.

Die volle Syntax des try..catch Statements ist:

try {
...
statements
...
}
catch(err_msg) {
...
statements
...
}

Die err_msg Zeichenkette im catch-Block enthält den Text, den AviSynth erzeugt, wenn der Fehler innerhalb des try Blocks aufgetreten ist. Dieser Text ist derselbe, der in der vertrauten MessageBox erscheinen würde, die sich zeigt, wenn sich ein schwerwiegender Skriptfehler ereignet.

Du kannst den Text abfragen (es ist eine normale String Variable), um bestimmte Unterzeichenketten im Inneren zu finden, die anzeigen, dass der Fehler aufgetreten ist. Das ist eine Technik, die viele nützliche Ergebnisse erzielen bringen kann (ein Beispiel siehe hier).

Andere Steurstrukturen (im weiteren Sinn)

Im weiteren Sinn gibt es viele Elemente in der AviSynth Syntax, die obwohl sie nicht selbst Strukturen steuern, zusammengenommen ermöglichen, Sprachkonstrukte zu erstellen, die einer Kontrollstruktur entsprechen. Diese Konstrukte wiederum ermöglichen die Durchführung komplexer Programmieraufgaben.

Die zu berücksichtigen Elemente sind die folgenden:

  1. Die Eval() Anweisung erlaubt die Ausführung von beliebigem Skript-Sprache Anweisungen (und ihr Verwandter Apply vereinfacht den Aufruf von Funktionen über ihren Namen).
  2. Mehrzeilen-Strings und insbesondere mehrzeilige Strings, die durch dreifache Anführungszeichen eingefasst sind (" " "), erlauben es, String-Literale in ihnen zu schreiben, die man in einem normalen AviSynth Skript nicht zulassen würde.
  3. Die Import() Anweisung ermöglicht das Ausführen beliebiger Skripte, die einen Wert zurückgeben können (nicht unbedingt einen Clip, ein Skript kann einen Wert jeglichen Typs zurückgeben, der einer Variablen des aufrufenden Skripts zugeordnet werden kann).
  4. Rekursion (die Fähigkeit, rekursive Funktionen zu erstellen).
  5. Steuerfunktionen, speziell Assert, Select, Default, NOP.

Die ersten drei erlauben es, einfache Block-Anweisungen wie Verzweigungsblöcke zu erstellen (analog zur if..elseif..else Steuerstruktur, der man häufig in Programmier-und Skriptsprachen begegnet). Ein einfaches Beispiel folgt unten (siehe den obigen Link für weitere Informationen)t:

# verschiedene Filterung definieren, basierend auf diesem Flag
heavy_filtering = true AviSource("c:\sources\meinequelle.avi")

# Das Ergebnis von Eval() einer Variable zuweisen, um es in einer Sondervariable zu bewahren
dummy = flag ? Eval("""
Levels(0, 1.2, 255, 20, 235)
stext = "schwer gefiltert"
Spline36Resize(720, 400)
""") : Eval("""
stext = "leicht gefiltert"
BicubicResize(720, 400)
"""
AddBorders(0, 40, 0, 40)
Subtitle(stext)

Das vierte (Rekursion) ist das allgemeinste Instrument, welches durch die Syntax bereit gestellt wird, um auf Sammlungen einzuwirken und Berechnungen beliebiger Komplexität durchzuführen. Es ist auch derzeit das einzig wirkliche Werkzeug.

Dies bedeutet nicht, dass man irgendetwas nicht in der AviSynth Script-Sprache tun könnte. In der Tat kann Rekursion zusammen mit Zuweisungen praktisch alles erreichen, was eine imperative Sprache mit Schleifenkonstrukten tun kann. Sie tut es nur einfach in einer Weise, mit der die meisten Menschen ohne besondere Programmierkenntnisse nicht vertraut sind, da die funktionale Programmierung eher eine spezielles Thema ist.

Das fünfte (Steuerfunktionen) sind die mehr oder weniger notwendigen Werkzeuge innerhab der obigen Konstrukte, um Input, Einstellwerte und Unterstützung des  ordnungsgemäßen Ablaufs des Konstrukts zu ermöglichen.

Schauen wir uns einige Beispiele für Rekursion an, um das allgemeine Muster zu erkennen, dem man folgen muss, um sie erfolgreich für seine Zwecke einzusetzen.

Beispiele 1: Eine Funktion erzeugen, die n-Mal eine Zeichenkette wiedeholt

Wir nehmen eine schon existierende Anwendung aus der AVSLib für unser Beispiel:

Function StrFill(string s, int count, bool "strict") {
strict = Default(strict, true)
Assert((strict ? count >= 0 : true), "StrFill: 'count' kann nicht negativ sein")
return count > 0 ? s + StrFill(s, count - 1) : ""
}

Die Rekursion ist der Aufruf, den die Funktion auf sich selbst macht in der return-Anweisung. Damit das richtig getan werden kann, muss die Reihenfolge der rekursiven Aufrufe schließlich mit einem einzigen Rückgabewert enden. Daher wird die return-Anweisung einer rekursiven Funktion immer einen bedingten Operator,?: verwenden.

Das ist alles, was über Rekursion zu sagen ist. Die beiden anderen Zeilen (wo das fünfte Element, die Steuerfunktionen verwendet werden) sind einfach nur dafür da, damit richtige Argumente hineingegeben werden. Das "strict"-Argument ist einfach ein Add-On für die Fälle, wo die Funktion still (ohne Auslösen eines Fehlers) einen leeren String zurück geben soll.

Beispiel 2: Eine Funktion erzeugen, die Frames eines Clips in freien Intervallen auswählt

Filter wie SelectEvery ermöglichen die effiziente Auswahl von beliebigen Sets von Frames. Sie verlangen aber, dass jeder Satz von Frames eine konstanten Abstand von seinem Nachfolger und Vorgänger hat (in anderen Worten, die Sätze sind in der Bildnummer periodisch). Um Frames mit unterschiedlichen Trennung (also nicht-periodisch) auszuwählen, müssen wir auf Skript-Funktionen zurückgreifen, die Rekursion verwenden.

Die untenstehende Funktion ist ein generisches Frameauswahl-Filter, das um beliebige Bilder auswählen zu können eine benutzerdefinierte Funktion verwendet (das func Argument muss ihren Namen enthalten), die das Intervall [s_idx..e_idx) auf die Frames legt, die ausgewählt wurden. func muss eine einzelne Ganzzahl als Argument akzeptieren und liefert die entsprechende zugeordnete Frame-Nummer zurück.

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)
}

Der rekursive Schritt (erste bedingte Verzweigung in der return -Anweisung) ist wieder ein Ausdruck, der die Funktion als Teilabschnitt umfasst. Dies ist im allgemeinen nicht nötig  (abhängig von der spezifischen Aufgabe, könnte auch nur die Funktion aufgerufen werden), aber es ist der übliche Fall beim Bau von komplexen Konstrukten.

Apply ruft die benutzerdefinierte Funktion, um die Frame-Nummer zu berechnen (eine robustere Implementierung würde diesen Aufruf in einen  try...catch -Block umschließen). Wenn die Framenummer innerhalb des Clips liegt, dann wird das zugehörige Frame ans Ergebnis angehängt, sonst endet die Rekursion.

Das folgende Beispiel soll den Aufbau verdeutlichen:

# mein eigener Selector (x^2)
Function CalcFrame(int idx) { return Int(Pow(idx, 2)) }

AviSource("my_200_frames.avi")
# wählt bis zu 20 Frames, die von CalcFrame gemappt werden
# in diesem Fall: 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196
FSelectEvery(last, "CalcFrame", 0, 20)

$Date: 2008/04/21 20:31:23 $