Intelligente Automation Next Funktion

Ist mir jetzt leider kein besserer Titel zu eingefallen. ::slight_smile:

Also habe mir jetzt mal folgendes ausgedacht wenn z.B: um :30 die Headlines eingefügt werden ohne dass das gerade eben gestartete Lied abgewürgt wird.

cf: http://forum.mairlist.com/index.php/topic,4822.0.html

Mit dem Insert Event wird ein Script gestartet welches den ‘OnTimer’ auf enable setzt.

  • Dieser liest die Gesamtzeit des aktuell laufenden Elements.
  • Falls Gesamtzeit < als “Toleranzzeit” then Exit
  • Im Timerintervall wird die “Elapsed”-Zeit des laufenden Elements gelesen
  • Ist Die Elapsedzeit > Toleranzzeit wird AutomationNext ausgeführt und Timer gestoppt.

Dies würde erlauben dass man eine Mindestlaufzeit festsetzen könnte.
Ich finde es persönlich besser dass ein Lied mindestens 1 Minute gelaufen ist, dann ausgeblendet wird und dann erst die Headlines laufen. Ob die um :30 oder um :31 laufen ist nicht so wichtig.
Hauptsache der eben gestartete Song wird nicht gleich wieder abgewürgt.

;D Vom Doktor bräuchte ich jetzt die Info wie man:

  • die Gesamtzeit des aktuell ausgegebenen Elements liest
  • dito für die Elapsedzeit

Gruss:
-Serge-

Einen Timer brauchst du gar nicht. Am einfachsten ist, man schaut, ob der aktuelle Player bereits mindestens eine Minute gelaufen ist. Wenn ja, fügt man das neue Element dahinter ein und löst man ein AutomationNext aus.

Falls der Player weniger als eine Minute gelaufen ist, fügt man das neue Element erstmal nur dahinter ein - und setzt den Fade-Out-Punkt des gerade laufenden Elementes auf 0:01:00. Außer natürlich, die Restspielzeit beträgt weniger als eine Minute, dann lassen wir den Player einfach zuende laufen.

Ich hab das mal fix zusammengehackt, siehe Script unten. Dort erkennst du auch, wie man die aktuelle Position ermittelt: Man vergewissert sich erst mit IsCueable, dass es sich um ein “cue-bares” Element handelt (anders als unendliche Streams, die keine Position haben). Wenn das der Fall ist, kann man den Player mit GetSource nach seinem IAudioSource fragem und den einen ICueableAudioSource typecasten, worauf man mit GetPosition die aktuelle Position abfragt.

Für die Restspielzeit zieht man die aktuelle Position einfach von Item.GetEffectivePlaybackEnd ab - das ist entweder Start Next, Fade Out, Cue Out oder EOF, je nachdem, was früher ist.

var
  newItem: IPlaylistItem;
  i: integer;
  activePlayer: integer;
  activeItem: IPlaylistItem;
  curpos: TTimeValue;

begin
  newItem := Factory.CreateFilePlaylistItem('d:\temp\test.mp3', []);

  CurrentPlaylist.BeginUpdate;
  try
    activePlayer := -1;
    for i := 0 to CurrentPlaylist.GetPlayerCount - 1 do
      if CurrentPlaylist.GetPlayer(i).GetState = psPlaying then begin
        activePlayer := i;
        break;
      end;

    if activePlayer = -1 then begin
      SystemLog('No player is active!');
      CurrentPlaylist.Insert(CurrentPlaylist.GetNextIndex, newItem);
      // Playlist will grab and play item automatically if automation is active,
      // no need to call AutomationNext here
      exit;
    end;

    // Index of item that is currently playing
    activeItem := CurrentPlaylist.GetPlayer(activePlayer).GetItem;

    // Insert new item below
    CurrentPlaylist.Insert(CurrentPlaylist.IndexOf(activeItem) + 1, newItem);

    if not activeItem.IsCueable then begin
      SystemLog('Active item is not cueable!');
      CurrentPlaylist.AutomationNext;
      exit;
    end;

    // Retrieve current playback position
    curpos := ICueableAudioSource(CurrentPlaylist.GetPlayer(activePlayer).GetSource).GetPosition;

    if activeItem.GetEffectivePlaybackEnd - curpos < SecondsToTimeValue(60) then begin
      SystemLog('Remaining time is below one minute.');
      exit;
    end;

    if curpos > SecondsToTimeValue(60) then begin
      SystemLog('Current player has played for at least one minute');
      CurrentPlaylist.AutomationNext;
      exit;
    end;

    SystemLog('Remaining time is over one minute, and current position is under one minute, setting cue point.');
    activeItem.SetCuePosition(ptFadeOut, activeItem.GetCuePosition(ptCueIn) + SecondsToTimeValue(60));

  finally
    CurrentPlaylist.EndUpdate;
  end;
end.

Ja Toll… Danke ;D
Das schaue ich mir morgen dann mal im Detail an…

Falls das so klappt wäre das 'ne ideale Lösung

Gruss:
-Serge-

Hi Torben,

habe mir das Script jetzt mal in Ruhe angesehen.
Folgendes möchte ich noch kurz diskutieren:

if not activeItem.IsCueable then begin SystemLog('Active item is not cueable!'); CurrentPlaylist.AutomationNext; exit; end;

Verstehe ich das jetzt richtig dass wenn es sich um einen Stream handelt wo momentan läuft dieser “gekicked” wird? Wieso?

Ich fasse mal kurz zusammen mit Referenz zum NewsContainer so dass jeder es auch versteht: :wink:

  1. der NewsContainer wird zunächst nur eingefügt.
  2. Ist die Restspielzeit des laufenden Elements < 60 Sekunden, dann wird nix gemacht, die News laufen ja dann in weniger als 60 Sekunden an.
  3. ist die “Elapsed”-Zeit des laufenden Elemets > 60 Sekunden dann wird das laufenden Element ausgeblendet und die News laufen dann sofort.
  4. Ist die Restzeit des laufenden Elementes > 60 Sekunden UND die momentane abgelaufenen Spielzeit <60 Sekunden wird beim momentan laufenden Element der Cue-Out auf 60 Sekunden gesetzt. Wird der erreicht, wird zum nächsten Element übergeblendet was die News sind.

… Genial !!!

Vielen lieben Dank für dieses tolle Script!

Gruss:
-Serge-

Zunächst etwas Theorie:

Der Begriff “cueable” bezeichnet in der mAirList-Welt ein Playlist-Element, das (a) eine fest definierte Länge hat und in dem man (b) Cuepunkte setzen kann (mit SetCuePosition). In der derzeiten Version sind alle spielbaren (“playable”) Elemente auch “cueable”, mit einer Ausnahme: Dem Element “Stream (unendlich)”.

Wird ein Element in einen Player geladen, so öffnet dieser es intern zweimal: Einmal für die normale Wiedergabe, und einmal für das PFL. Das geöffnete Element ist in einem Objekt vom Typ “IAudioSource” gekapselt, das so Methoden hat wie “Play”, “Pause”, “SetVolume” usw. Die beiden Sources für die Wiedergabe bzw. PFL kann man mit Player.GetSource und Player.GetPFLSource abfragen.

Handelt es sich bei dem Element nun um ein “cueable”-Element, dann unterstützt sein Source auch das Interface “ICueableAudioSource”, über das man mit einem Typecast zugreifen kann (siehe mein Script oben). Dort gibt es dann weitere Methoden, insbesondere GetDuration, GetPosition und SetPosition.

Ich habe gerade mal eine aktualisierte Script-“Hilfe” (Interface-Referenz) hochgeladen, da kannst du das alles nachschlagen: http://www.mairlist.com/download/mAirList/v3.1/scriptinghelp/mAirListScript.chm

Nun zur Praxis:

Das Script hat ja die Aufgabe zu entscheiden, ob das gerade laufende Element sofort durch ein AutomationNext unterbrochen werden soll, oder ob es noch (ggf. eine begrenzte Zeit) weiterlaufen soll. Wenn letzteres der Fall ist, wird auf das sofortige AutomationNext verzichtet, und das News-Element wird einfach nur eingefügt, sowie ggf. noch ein Fade-Out-Punkt beim gerade laufenden Element gesetzt.

Was passiert nun, wenn gerade ein “Stream (unendlich)” läuft? Dieser ist nicht “cueable”, hat also weder eine Länge noch eine Position noch Cuepunkte. Er endet auch nicht von alleine (außer die Verbindung bricht unwiderruflich ab), sondern er kann nur durch ein Event oder durch Benutzereingriff gestoppt werden. Einen Fade-Out-Punkt kann man auch nicht setzen, weil er nicht “cueable” ist und keine Cuepunkte unterstützt. Das Script entscheidet sich daher in diesem Fall dafür, sofort ein AutomationNext auszulösen.

Danke für die Ausführung.

OK, ich denke das kann man dann entscheiden ob man davon ausgehen soll, dass man ein Element vom Typ “Stream (unendlich)” in der Playliste hat.
Wir wahrscheinlich nicht und deshalb kann das Script dann auch so bleiben.

Ein Stream wird bei uns momentan noch nicht aufgeschaltet. Eventuell in Zukunft wenn mAirList ausschliesslich zum Einsatz kommt zum zustreamen einzelner Sendungen.
Dann dürfen die Events sowieso nicht laufen, das ist dann die Aufgabe des Moderators am Stream.

Wobei mir jetzt noch einfällt: Wie kann ich die “Item Class” des Elements abfragen?
Möchte wissen ob das aktuelle Element vom Typ “Stream, Silence, File, …” ist.

Und bei der Container-Erstellung ist mir der Tag aufgefallen welcher fix auf 2 Sekunden steht. Der ist über die GUI auch nicht einstellbar (oder ich habe den zumindest nicht gefunden)
Wird der berücksichtigt? Ich kann nicht feststellen dass innerhalb vom Container zwichen den Tracks 2 Sekunden gefadet wird…

Gruss:
-Serge-

Problem,

der News Container wird immer als “gespielt” in die Playliste eingefügt.
Denke mal das Problem liegt hier:

      CurrentPlaylist.Insert(CurrentPlaylist.GetNextIndex, newItem);

GetNextIndex… Resultat der “Verschlimmbesserung” wie in diesem Thread erwähnt?
http://forum.mairlist.com/index.php/topic,4833.0.html
Benutze momentan noch b820 zum testen

Egal wie, wenn ich das Script folgendermassen umändere läuft alles wie es soll (bis auf die Kosmetik dass der Container oben in der Playliste eingefügt wird)

[code]var

i: integer;
activePlayer: integer;
activeItem: IPlaylistItem;
curpos: TTimeValue;

begin

CurrentPlaylist.BeginUpdate;
try
activePlayer := -1;
for i := 0 to CurrentPlaylist.GetPlayerCount - 1 do
if CurrentPlaylist.GetPlayer(i).GetState = psPlaying then begin
activePlayer := i;
break;
end;

if activePlayer = -1 then begin
  SystemLog('No player is active!');
  CurrentPlaylist.InsertFile(0, 'C:\mymAirListContainers\News_Headlines_Container_no_Fixtime.mlp');
  // Playlist will grab and play item automatically if automation is active,
  // no need to call AutomationNext here
  exit;
end;

// Index of item that is currently playing
activeItem := CurrentPlaylist.GetPlayer(activePlayer).GetItem;

// Insert new item below
CurrentPlaylist.InsertFile(0, 'C:\mymAirListContainers\News_Headlines_Container_no_Fixtime.mlp');

if not activeItem.IsCueable then begin
  SystemLog('Active item is not cueable!');
  CurrentPlaylist.AutomationNext;
  exit;
end;

// Retrieve current playback position
curpos := ICueableAudioSource(CurrentPlaylist.GetPlayer(activePlayer).GetSource).GetPosition;

if activeItem.GetEffectivePlaybackEnd - curpos < SecondsToTimeValue(60) then begin
  SystemLog('Remaining time is below one minute.');
  exit;
end;

if curpos > SecondsToTimeValue(60) then begin
  SystemLog('Current player has played for at least one minute');
  CurrentPlaylist.AutomationNext;
  exit;
end;

SystemLog('Remaining time is over one minute, and current position is under one minute, setting cue point.');
activeItem.SetCuePosition(ptFadeOut, activeItem.GetCuePosition(ptCueIn) + SecondsToTimeValue(60));

finally
CurrentPlaylist.EndUpdate;
end;
end.[/code]

Nein, das Problem ist nicht GetNextIndex. Das Problem liegt hier:

  newItem := Factory.CreateFilePlaylistItem('d:\temp\test.mp3', []);

Diese Syntax funktioniert nur für “echte” Audiodateien. Es wird immer ein Playlist-Element vom Typ “Datei” erzeugt. Wenn du eine .mlp-Datei angibst, baut mAirList dir zunächst brav das Element zusammen (die Dateiendung wird nicht weiter überprüft); wenn es dann aber abgespielt werden soll, meldet die BASS.DLL einen Fehler, und das Element fliegt sofort wieder aus der Playlist raus. Im Systemprotokoll solltest du sowas lesen können wie “Überspringe fehlerhaftes Element xyz”.

Was du eigentlich erzeugen möchtest, ist ein ContainerPlaylistItem, in das die angegebene Playlist-Datei als Inhalt geladen wird. Anders als CreateFilePlaylistItem ist die Methode Playlist.InsertFile, die du jetzt nutzt, so intelligent und erkennt, dass es sich um eine Playlist handelt, und macht gleich einen Container daraus.

Im Prinzip sollte es daher reichen, du nutzt tatsächlich InsertFile, gibst aber nicht 0 als Position an, sondern wie in meinem Script entweder “GetNextIndex” oder “IndexOf(activeItem) + 1”.

Alternativ, und das ist beinahe hübscher, kannst du den Container auch manuell erzeugen. Das hat den Vorteil, dass man gleich noch den Titel usw. setzen kann:

var
  newItem: IContainerPlaylistItem;
  ..

begin
  newItem := Factory.CreateContainerPlaylistItem;
  newItem.GetPlaylist.LoadFromFile('C:\mymAirListContainers\News_Headlines_Container_no_Fixtime.mlp');
  newItem.SetTitle('Headlines');

  (weiter wie oben)
end.

OK verstanden.

Container ist immer der gleiche und ist schön getagged, mit Icon versehen, eigene Farbe, etc…

Ich versuche das jetzt mal.

Gruss:
-Serge-

Ah, Moment… Ist die .mlp-Datei der Inhalt des Containers (davon war ich jetzt ausgegangen), oder ist er in der .mlp-Datei enthalten?

Wenn letzteres, dann änder das Script wie folgt:

[code]
var
newItems: IPlaylist;

begin
newItems := Factory.CreatePlaylistFromFile(‘C:\mymAirListContainers\News_Headlines_Container_no_Fixtime.mlp’);

// und dann überall statt Insert(xyz, newItem):
CurrentPlaylist.InsertList(xyz, newItems);

end.[/code]

Der Container ist eine eigene .mlp Datei wo um xx:30 mit dem script geladen wird.

newItem.GetPlaylist.LoadFromFile('C:\mymAirListContainers\News_Headlines_Container_no_Fixtime.mlp');

müsste also passen. Bin leidr noch nicht dazu gekommen, aber ich teste das noch nachher und berichte dann.

Auf jeden Fall bin ich jetzt ein gutes Stück weiter…

Gruss:
-Serge-

Irgendwas klemmt. Bekomme immer “Invalid Number of Parameters”

Aber hiermit klappt’s genau so wie gewünscht. Der Container (als .mlp abgespeichert) wird schön an der nächsten Stelle in der Playliste eingefügt.

    CurrentPlaylist.InsertFile(CurrentPlaylist.IndexOf(activeItem) + 1, 'C:\mymAirListContainers\News_Headlines_Container_no_Fixtime.mlp');

Ich bring die Sache hier noch einmal hoch:

[code] if activeItem.GetEffectivePlaybackEnd - curpos < SecondsToTimeValue(60) then begin
SystemLog(‘Remaining time is below one minute.’);
exit;
end;

if curpos > SecondsToTimeValue(60) then begin
  SystemLog('Current player has played for at least one minute');
  CurrentPlaylist.AutomationNext;
  exit;
end;

SystemLog('Remaining time is over one minute, and current position is under one minute, setting cue point.');
activeItem.SetCuePosition(ptFadeOut, activeItem.GetCuePosition(ptCueIn) + SecondsToTimeValue(60));

[/code]

Zur Erinnerung, es soll folgendes erreicht werden:

  1. der NewsContainer wird zunächst nur eingefügt.
  2. Ist die Restspielzeit des laufenden Elements < 60 Sekunden, dann wird nix gemacht, die News laufen ja dann in weniger als 60 Sekunden an.
  3. ist die “Elapsed”-Zeit des laufenden Elemets > 60 Sekunden dann wird das laufenden Element ausgeblendet und die News laufen dann sofort.
  4. Ist die Restzeit des laufenden Elementes > 60 Sekunden UND die momentane abgelaufenen Spielzeit <60 Sekunden wird beim momentan laufenden Element der Cue-Out auf 60 Sekunden gesetzt. Wird der erreicht, wird zum nächsten Element übergeblendet was die News sind.

Aufgefallen ist jetzt bei Punkt 4, dass wenn das Script bei ca. 00:00:59 anläuft, der Fade-out Punkt gesetzt wird (auf 00:01:00), diese Marke aber in Zwischenzeit schon passiert ist und der Song weiterläuft.
Um dies zu Verhindern ist es besser den Fade-out Punkt auf 00:01:02 zu setzen, dann klappt’s auf jeden Fall und die 2 Sekunden tun auch nicht weh.

    activeItem.SetCuePosition(ptFadeOut, activeItem.GetCuePosition(ptCueIn) + SecondsToTimeValue(62));

Gruss:
-Serge-