So umfangreich die Funktionen in mAirList auch sind, so fehlt doch immer wieder mal das eine oder andere Feature für den gewünschten Zweck. Macht aber nichts, denn genau hierfür gibt es die Scripting-Engine, mit welcher die Möglichkeiten in mAirList noch wesentlich umfangreicher werden als ohnehin schon. Also:
Zustände erfassen, bearbeiten und melden
Es geht hier im wesentlichen um die Fernsteuerung gewisser Funktionen, dazu haben viele Nutzer die berühmte Kassentastatur am Start. Möchte ich beispielsweise mittels Taste in mAirList einen Player starten, kann ich in der Konfiguration dem entsprechenden Hotkey den Befehl
PLAYER 1-1 START
zuordnen. Soll der Player wieder anhalten, so weist man der Taste nebenan
PLAYER 1-1 STOP
zu. Für eine Pausenfunktion nehmen wir eine dritte Taste:
PLAYER 1-1 PAUSE
Dann funktioniert die Geschichte wie an einem CD-Spieler: Taste Start, Taste Stop, Taste Pause – alles gut.
Jetzt will ich die ganze Musik aber (auch) über einen Faderkontakt steuern, also Fader auf: START
, Fader zu: STOP
. Soweit in Ordnung, das trage ich in der Konfiguration unter dem entsprechenden Fernsteuergerät ein.
Was mache ich jetzt aber mit dieser vermaledeiten PAUSE
?
Der eine mag sagen: „Brauchst Du doch gar nicht!“ Ich aber sage Euch: „Brauche ich doch!“ Und zwar nicht nur, weil ich das aus Prinzip so haben will, sondern weil es tatsächlich Situationen gibt, in denen eine PAUSE
das Mittel der Wahl ist – ein Stichwort dazu wäre „Falschfahrermeldung während einer längeren vorproduzierten Gesprächssendung“.
Es wird also interessant, denn meine Aktion „Regler zu“ soll jetzt einmal zum STOP
führen, das andere mal jedoch (während des Falschfahrers nämlich) zu PAUSE
. In der Konfiguration kann man aber für „Regler zu“ nur einen einzigen Befehl hinterlegen – entweder, oder. Hm.
Was ich will, ist ein Umschalter, mit dem ich mir aussuchen kann, ob beim „Regler zu“ der Player in STOP
oder in PAUSE
geht!
Und jetzt kommt die mAirlistsche Genialität ins Spiel: Ich kann nämlich nach Belieben neue Befehle erfinden und in Skripten weiterverarbeiten!
Sacken lassen – okay. Was heißt das jetzt konkret? Nun, ich trage in der Konfiguration für die Fernsteuerung nun nicht mehr PLAYER 1 STOP
(oder eben … PAUSE
) ein, sondern denke mir etwas passendes aus, hier:
FADER 1 ZU
(Der steht natürlich nicht im Dropdown-Menü, den schreibt man einfach ins Feld rein.) Ziehe ich den Regler herunter, wird ebendieser Befehl nach mAirList weitergereicht. Passieren tut freilich gar nix, weil das Programm nichts damit anfangen kann, hat es doch noch nie was von FADER 1 ZU
gehört. Und da kommt jetzt das Skript in Spiel: Wir sagen mAirList mit dessen Hilfe, wie es diesen Befehl zu interpretieren hat. Dafür gibt es die Prozedur OnExecuteCommand
, die darauf wartet, daß irgendwann ein Fernsteuerbefehl gegeben wird und dann etwas tut, z. B. weitere Befehle ausführen, nämlich mit dem Kommando ExecuteCommand('<Befehl>')
. Im einfachsten Falle sähe das so aus:
procedure OnExecuteCommand(Command: string);
begin
if Command = 'FADER 1 ZU' then begin
ExecuteCommand('PLAYER 1-1 STOP');
end;
end;
Nun weiß mAirList wieder, daß es bei „Regler zu“ den Player stoppen soll. Super.
Allerdings sind wir jetzt wieder gerade gleich weit wie vorher, nur komplizierter – von PAUSE
keine Spur! Moment, es geht sofort weiter:
Wir müssen mAirList also beibringen, was wir vorhaben und was es davon abhängig tun soll. Ich nehme dazu eine Taste auf dem Keyboard her, beschrifte sie mit „Pause“ und weise ihr in der Konfiguration den (erfundenen) Befehl PAUSE
zu.
Wenn ich die drücke, dann soll bei „Regler zu“ der Player in PAUSE
gehen, und wenn ich sie nochmal drücke, dann soll eben STOP
ausgeführt werden undsoweiter. mAirList braucht dafür eine Art Gedächtnis, einen Speicher, in dem es den momentanen Zustand ablegen kann und anhand dessen das weitere Vorgehen bestimmt.
Dieser Speicher ist eine Variable, die beliebige Werte annehmen kann, zum Beispiel (Wurzel aus 99)
oder π
oder 'Marmorkuchen'
. Für unsere Zwecke beschränken wir uns auf die beiden Zustände wahr
oder falsch
. (Allerdings möchte mAirList, daß wir englisch mit ihm reden: true
oder false
.) Diese Variable, nennen wir sie ModePauseOn
, müssen wir mAirList erstmal eintrichtern:
var
ModePauseOn: boolean;
Dieser Variablen können wir jetzt einen der zwei Zustände true
oder false
zuweisen (boolean), das geht, indem man ein Gleichheitszeichen mit einem Doppelpunkt davor verwendet:
ModePauseOn := true
Und, jetzt kommt’s, wir können sie auch wieder abfragen, nämlich so:
if ModePauseOn = true then begin
ExecuteCommand('<irgendein Kommando>');
ExecuteCommand('<noch ein Kommando>');
end
else if ModePauseOn = false then begin
ExecuteCommand('<ein anderes Kommando>');
end;
undsoweiter. Hier gibt es mehrere Dinge zu beachten: Vor einem else
darf das end
kein Semikolon besitzen, das kommt erst beim letzten end
der gesamten if
-Abfrage! Und: Man kann die Schreibweise auch ein bißchen komprimieren, das ist übersichtlicher bei komplizierteren Ausdrücken. Das sähe dann so aus (bei identischer Funktionalität):
if ModePauseOn then begin
ExecuteCommand('<irgendein Kommando>');
ExecuteCommand('<noch ein Kommando>');
end
else if NOT ModePauseOn then begin
ExecuteCommand('<ein anderes Kommando>');
end;
Und mit diesem Wissen können wir jetzt die gewünschte Funktion realisieren:
var // Hier lernt mAirList die neue Variable kennen.
ModePauseOn: boolean;
procedure OnLoad; // Hier wird sie beim Programmstart erstmal auf false gesetzt.
begin
ModePauseOn := false;
end;
procedure OnExecuteCommand(Command: string);
begin
if NOT ModePauseOn AND (Command = 'PAUSE') then begin // Hier wird der Zustand gespeichert.
ModePauseOn := true;
end
else if ModePauseOn AND (Command = 'PAUSE') then begin
ModePauseOn := false;
end
else if ModePauseOn AND (Command = 'FADER 1 ZU') then begin // Hier wird mit dem gespeicherten Wissen etwas angefangen.
ExecuteCommand('PLAYER 1-1 PAUSE');
end
else if NOT ModePauseOn AND (Command = 'FADER 1 ZU') then begin
ExecuteCommand('PLAYER 1-1 STOP');
end;
end;
Auch hier sind Dinge zu beachten: Bei verknüpften if
-Bedingungen müssen Klammern gesetzt werden. Damit beim Programmstart klare Verhältnisse herrschen, ist die procedure OnLoad
eingebaut, die beim Programmstart die Variable erst mal auf false
setzt. Und: das abschließende
begin
end.
(mit Punkt hinten) habe ich in meinen Beispielen weggelassen. Gehört aber immer dazu.
Das sollte funktionieren (bei mir tut es das jedenfalls), allerdings hat diese Konstruktion noch ein entscheidendes Manko: Woher weiß ich denn nach wieoft Taste drücken, welcher Zustand gerade eingestellt ist? Ausprobieren wäre eine Lösung, in der Livesendung aber eher nicht ratsam. Wir brauchen also eine Rückmeldung, ein Lämpchen oder so was, welcher Zustand gerade anliegt.
Ist mAirList, geht! Und dazu bauen wir uns einen sogenannten Button ein: In der Konfiguration unter GUI > Bildschirmobjekte > Hinzufügen > Erweiterter Button
. Hier zunächst unter Aussehen
sein Erscheinungsbild festlegen: Standardtext
wird „Pause“, das soll hinterher so draufstehen. Bei Button-Art
„Umschalt-Button“ wählen, Bei Programmstart aktiv
nicht anhaken und Aktionen ausführen bei Fernsteuerung
auch nicht. (Warum, erkläre ich später.) Im folgenden werden die Farben des Buttons und der Schrift festgelegt, insbesondere unter Inaktiv
und Aktiv
– sagen wir, für Inaktiv
irgendein grau,
und für Aktiv
knallgelb:
Größe und Plazierung werden im Layout-Editor bestimmt, das lasse ich an dieser Stelle mal weg.
Das ist bis hierher schon sehr schön, aber noch ist der Button nichts als ein farbiges Feld mit einem Wort drin geschrieben. Um ihm Leben einzuhauchen, benötigen wir die beiden anderen Reiter in der Konfiguration, Aktionen
und Erweitert
.
Unter Aktionen wenn angeklickt (aktiviert)
fügen wir den Befehl PAUSE
hinzu (Hinzufügen > Verschiedenes > Befehl ausführen > „PAUSE“ eintippen
).
Dasselbe auch unter Aktionen wenn deaktiviert
, bitte.
Unter Erweitert
ist noch ganz wichtig ID für Fernsteuerung
! Hier schreiben wir BUTTON.PAUSE
hinein. Der Rest ist Geschmacksache.
Solchermaßen gerüstet, machen wir uns wieder ans Skript und tragen nach:
var
ModePauseOn: boolean;
procedure OnLoad;
begin
ModePauseOn := false;
ExecuteCommand('BUTTON.PAUSE OFF'); // <-- Neu!
end;
procedure OnExecuteCommand(Command: string);
begin
if NOT ModePauseOn AND (Command = 'PAUSE') then begin
ModePauseOn := true;
ExecuteCommand('BUTTON.PAUSE ON'); // <-- Auch neu!
end
else if ModePauseOn AND (Command = 'PAUSE') then begin
ModePauseOn := false;
ExecuteCommand('BUTTON.PAUSE OFF'); // <-- Das auch.
end
else if ModePauseOn AND (Command = 'FADER 1 ZU') then begin
ExecuteCommand('PLAYER 1-1 PAUSE');
end
else if NOT ModePauseOn AND (Command = 'FADER 1 ZU') then begin
ExecuteCommand('PLAYER 1-1 STOP');
end;
end;
Soweit klar, oder? Die ID für Fernsteuerung
ist nichts weiter als ein Befehl, den wir in gewohnter Weise verarbeiten können.
Und jetzt kommt der große Augenblick: Beim drücken der Taste „Pause“ leuchtet der Button gelb auf, und die Reglermimik wird auf „Pause“ gestellt. Nochmals drücken – der Button erlischt, bei „Regler zu“ gilt wieder STOP
. Hurra!
(Und soll es tatsächlich ein Lämpchen sein, welches aufleuchten soll, dann trage eben noch das passende Kommando für das entsprechende Interface ein!)
Es kommt aber noch besser: Das ganze funktioniert auch, indem man mit der Maus auf den Button klickt! Will man das nicht, muß man bei Aktionen …
die Eingabe des Befehls weglassen, oder – eleganter – man verwendet im Skript an passender Stelle das Kommando ExecuteCommand('BUTTON.PAUSE DISABLE')
, dann wird das ganze sogar steuerbar. (Einschalten entsprechend mit … ENABLE
.)
(Eine Erklärung noch nachgereicht: Weiter oben ging es darum, bei der Konfiguration des Buttons Aktionen ausführen bei Fernsteuerung
nicht anzuhaken. Täten wir es, so würde beim Befehl BUTTON.PAUSE ON
dessen Befehl PAUSE
gleich wieder gegeben. Da dieser Befehl kurz zuvor aber schon im Skript stattfand, würde gar nichts passieren, da die Funktion zweimal geschaltet, also gleich wieder ausgetogglet würde.)
Dieses Prinzip kann nun beliebig ausgebaut werden, indem für jede Zustandsebene eine neue Variable eingeführt wird:
var
ModePauseOn,ModePflOn,ModeEditOn: boolean;
Damit könnte man dann seinen Tasten für jeden Zustand jeweils andere Funktionen zuordnen, z. B. beim Vorhören soll die Taste „Begin“ die Abspielposition auf Null stellen (PFL 0
), im Zustand „Edit“, der aber nur einschaltet werden können soll, wenn „PFL“ aktiv ist, soll er die eingestellte Cue-Position löschen (PFL CUEIN DELETE
). Bei mir sieht das dann so aus:
else if NOT ModePflOn AND NOT ModeCartOn AND (Command = 'PFL') then begin
ModePflOn := true;
ExecuteCommand('BUTTON.PFL ON');
ExecuteCommand('BUTTON.EDIT ENABLE'); // <-- Hier wird der EDIT-Button freigegeben
ExecuteCommand('PLAYLIST 1 EXTRAPFL ON');
end
…
else if ModePflOn AND ModeEditOn AND (Command = 'BEGIN') then begin
ExecuteCommand('PFL 0');
end
…
else if ModePflOn AND NOT ModeEditOn AND (Command = 'EDIT') then begin
ModeEditOn := true;
ExecuteCommand('BUTTON.EDIT ON');
end
…
else if ModeEditOn AND NOT ModePoolOn AND (Command = 'BEGIN') then begin
ExecuteCommand('PFL CUEIN DELETE');
end
…
(Hier sind noch weitere Spezialitäten wie „Pool“ und „Cart“ im Einsatz, da kann man drüber weglesen.)
All das hat noch einen weiteren Nebeneffekt: Im allerersten Beispiel mit Start und Stop des Players verbraucht man auf seiner Tastatur zwei Tasten. Hat man drei Player, so sind es zusammen schon sechse! Und so läppert sich das zusammen, bis man eine größere Tastatur kaufen muß, die aber wieder nirgendwohin paßt. Wir (ha!) können jetzt aber diese Start-/Stop-Funktion auf eine einzige Taste legen und haben damit die Hälfte der Tasten eingespart!
(Ja ich weiß: Dafür gibt es den Befehl PLAYER m-n START/STOP
. Aber es gibt in mAirList auch Befehle, bei denen das togglen nicht von Hause aus zur Verfügung steht, und dann seid Ihr mit dem Skript wieder ganz vorne dabei.)
So, hiermit sei es erstmal genug. Frohes Skriptbasteln wünscht Euch,
mit getoggleten Grüßen,
TSD
Edit: Typos mal wieder. Haufenweise.