MeMP - Mein einfacher Mp3-Player
Kapitel 10. Eine Playlist
Den wirklich komplizierten Teil haben wir jetzt hinter uns. Wir leiten jetzt von unserer Player-Klasse eine Playlist-Klasse ab, die eine Liste von Dateien verwalten und abspielen kann. Mit der ganzen Vorarbeit, die wir bis jetzt geleistet haben, ist das nicht mehr schwer. Und keine Angst: Das wird die letzte Klasse, die wir erstellen. Eine Instanz dieser Klasse wird dann den Hauptteil unseres Players bilden.
TPlaylistAddItemEvent
= procedure(Sender: TObject; NewAudioFile: TAudioFile) of Object;
TMeMPPlaylist = Class(TMeMPPlayer)
private
fPlayingIndex: Integer;
fPlayingFile: TaudioFile;
fPlaylist: TObjectlist;
// einige Events
fOnClear : TNotifyEvent;
fOnAddItem : TPlaylistAddItemEvent;
function GetNextAudioFileIndex: Integer;
function GetPrevAudioFileIndex: Integer;
public
property PlayingFile: TAudioFile read fPlayingFile;
property PlayingIndex: Integer read fPlayingIndex;
property OnClear: TNotifyEvent read fOnClear write fOnClear;
property OnAddItem: TPlaylistAddItemEvent read fOnAddItem write fOnAddItem;
constructor Create;
destructor Destroy; override;
procedure Add(aAudiofileName: String);
procedure Play(aIndex: Integer); Overload;
procedure PlayNext;
procedure PlayPrevious;
procedure Clear;
end;
Wir haben hier direkt zwei weitere Events eingefügt, die wir benötigen. Die beiden anderen Properties geben uns Auskunft darüber, an welcher Stelle wir uns in der Playlist befinden, bzw. welche Datei gerade abgespielt wird.
Hinweis: PlayingFile entspricht meist dem fMainAudioFile der zugrunde liegenden Playerklasse. Der Unterschied besteht darin, dass Playingfile nur eine Referenz auf ein Objekt in der Liste ist, um z.B. den Eintrag dazu in einer Listview anders zu zeichnen. Mit der Trennung wird es möglich sein, die Playlist zu leeren, ohne ggf. die laufende Wiedergabe abzubrechen. In dem Fall kennt der Player noch die Datei (der hat sie sich ja kopiert), die Playlist hat sie aber schon „vergessen“. Wir müssen bei Erweiterungen der Klasse darauf achten, dass PlayingFile immer auf ein gültiges Objekt verweist oder NIL ist. Es ist sinnvoll, bei der Erweiterung der Klasse darauf zu achten, dass PlayingFile und PlayingIndex immer so weit wie möglich konsistent sind.
Im Konstruktor erzeugen wir die interne ObjectList und initialisieren das PlayingFile mit Nil. Im Destruktor räumen wir wieder auf.
Die Methode Add erstellt aus dem übergebenen Dateinamen ein Objekt der Klasse TAudioFile, ermittelt die Informationen aus dieser Datei, fügt es der Liste hinzu und löst dann das OnAddItem-Event aus.
procedure TMeMPPlaylist.Add(aAudiofileName: String);
var NewFile: TAudioFile;
begin
NewFile := TAudioFile.Create;
NewFile.GetAudioInfo(aAudiofileName);
fPlaylist.Add(NewFile);
if assigned(fOnAddItem) then
fOnAddItem(Self, NewFile);
end;
Zum Abspielen übergeben wir einfach nur den Index der Datei, die wir abspielen wollen. Wir erlauben an dieser Stelle auch einen negativen Index. In diesem Fall wird das zuletzt abgespielte Stück erneut abgespielt. Die Bedingungen davor sollten sicherstellen, dass Fehleingaben abgefangen werden.
procedure TMeMPPlaylist.Play(aIndex: Integer);
begin
if aIndex >= 0 then
fplayingIndex := aIndex;
if (fplayingIndex > fPlaylist.Count - 1) and (fPlaylist.Count > 0) then
fplayingIndex := fPlaylist.Count - 1;
if (fplayingIndex >= 0) and (fPlayingIndex < fPlaylist.Count) then
fPlayingFile := TAudioFile(fPlaylist[fplayingIndex]);
play(fPlayingFile);
end;
PlayNext und PlayPrevious sind schnell erklärt – es wird einfach das nächste bzw. vorherige Stück in der Liste abgespielt.
procedure TMeMPPlaylist.PlayNext;
begin
Play(GetNextAudioFileIndex);
end;
procedure TMeMPPlaylist.PlayPrevious;
begin
Play(GetPrevAudioFileIndex);
end;
Die hier aufgerufenen privaten Methoden GetNextAudioFileIndex und GetPrevAudioFileIndex liefern im einfachsten Fall einfach fPlayingIndex +/- 1 zurück. Wir könnten hier auch optional eine Zufallswiedergabe einbauen.
Zuletzt die Methode Clear, die den Player anhält und die Playlist in den ursprünglichen Zustand zurückversetzt.
procedure TMeMPPlaylist.Clear;
begin
Stop;
fPlaylist.Clear;
if Assigned(fOnClear) then
fOnClear(Self);
fPlayingFile := Nil;
fPlayingIndex := 0;
end;