Ausgangssituation:
Ich habe 14 Titel in der Playlist.
Starte den ersten , befinde mich im Assist-Mode.
Wenn dieser Titel zu Ende ist, wird ja ‘OnPlayerStop’ ausgelöst.
Aber warum ist die Anzahl der Einträge in der Playlist dann immer noch 14, obwohl der Titel gespielt wurde und aus der Playlist entfernt wurde.
Ich hätte vermutet, das hier dann eben als ‘CurrentPlaylist.GetCount’ nur noch 13 vorhanden sind.
Ich vermute irgendwo ein Timing-Problem.
Wenn dem so ist, wie lässt sich das umgehen ?
Folgendes Hintergrund-Script als Beispiel:
procedure OnPlayerStop(PlaylistIndex: integer; PlayerIndex: integer; Duration: TTimeValue; Item: IPlaylistItem);
var
playlist_count: Integer;
idx:Integer;
pi: IPlaylistItem;
titel, artist: string;
begin
playlist_count := CurrentPlaylist.GetCount;
SystemLog('playlist_count ' + IntToStr(playlist_count));
while idx < playlist_count do
begin
pi := CurrentPlaylist.GetItem(idx);
titel := pi.GetTitle;
artist := pi.GetArtist;
SystemLog(titel + '-' + artist);
idx := idx + 1;
end;
end;
Läßt Du abgespielte Elemente anzeigen? Die sollten mitzählen. Setze probeweise die Anzahl der gespielten Elemente auf null. Möglicherweise könntest Du von playlist_count den Wert CurrentPlaylist.GetHistoryCount abziehen. (Ist aber in die Bläue hinein, ich kann gerade nichts ausprobieren.)
Ich könnte mir vorstellen, daß zum Zeitpunkt der „Player Stop“-Operation der Titel ja noch im Player (und damit in der Playlist) ist und erst danach daraus entfernt wird. Setze mal Sleep(100); in die erste Zeile.
Damit habe ich in der Zwischenzeit schon experimentiert.
Und es geht damit in der Tat.
Problem ist halt immer bei solchen ‘Kunstgriffen’ man weiß nie wie lange die funktionieren
Vielleicht fällt @Torben ja noch eine andere Lösung ein.
Nunja, man zwingt ja den aktuellen Thread zum warten.
Nun kenn ich natürlich nicht die Implementierungen in Mairlist, aber ‘auf etwas warten’ kann schon hin und wieder unerwünschte Nebeneffekte mit sich bringen.
Daher versuche ich so etwas meist bei der Programmierung zu vermeiden.
Klar, es handelt sich hier nur um ein paar Millisekunden.
Da ist korrekt. Die Benachrichtigung geht gleichzeitig an das Script wie auch an den Verarbeitungsthread der Playlist, der den Player dann entlädt und ggf. neu belegt. Was davon zuerst passiert, ist mehr oder weniger Zufall.
Wenn du darauf wartest, dass das gerade gestoppte Element aus der Playlist entfernt wurde, könntest du das zum Beispiel in einer Schleife tun:
while CurrentPlaylist.Contains(Item) do Sleep(100);
Nur Vorsicht, auch das Pausieren des Players löst OnPlayerStop aus, und dann bleibt das Element in der Playlist stehen, und das Script landet in einer Endlosschleife. Um diesen Fall abzufangen, wäre es sinnvoll, einen Zähler mit in o.g. Schleife einzubauen, und nach x Runden abzubrechen.
Was jetzt in meinen Augen eher nach „Kunstgriff“ aussieht.
Aber @ssnoopy, Du könntest natürlich das Argument von Sleep schrittweise verkleinern, bis es eben noch zuverlässig funktioniert, falls Dir eine Zehntelsekunde zu lang ist. Ich bin sicher, da geht noch was.
Algorithmen zur Vermeidung von Synchronisationsproblemen bei Nebenläufigkeit sind immer in irgendeiner Weise “Kunstgriff”. Da gibt es ganze Bücher drüber.
Das hier gezeigte “busy waiting” ist ein Standard-Pattern, nicht fürchterlich effizient, aber allgemein als Methode anerkannt.
Natürlich kann auch ein einfaches Sleep in diesem Fall ausreichend sein - aber ein Wort der Warnung: Es gibt auf keine Garantie, in welcher Zeit die Verarbeitung der Playlist abgeschlossen ist. Du musst also selbst die für dich passende Anzahl Millisekunden ermitteln; und selbst dann kann es vorkommen, dass es im Einzelfall mal länger dauert.
Daher ist ein Algorithmus, der explizitig auf das Verschwinden des Elementes aus der Playlist wartet, die einzige wasserdichte Methode.
Danke, Torben, für diese Erklärung. Das ist natürlich richtig, aber ich denke, daß @ssnoopy mit der Routine keinen Reaktor-SCRAM o. ä. auslösen möchte. Daher sah ich den Einzeiler, der einerseits wohl hinreichend lang, andererseits aber auch kurz genug für ssnoopys Belange ist, vorzuschlagen.
Aber alles in allem wieder einmal ein lehrreicher Einblick in die Arbeit des Programmierens!
procedure OnPlayerStateChange(PlaylistIndex: integer; PlayerIndex: integer;
OldState: TPlayerState; NewState: TPlayerState; Item: IPlaylistItem);
begin
if NewState = psPlaying then
begin
if Item.GetArtist = 'Kraftwerk' then
// usw.