Scripting-Hilfe: Zustände erfassen, bearbeiten und melden

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.

Pausentaste

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,

Pause%20aus

und für Aktiv knallgelb:

Pause%20ein

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.

1 Like