MeMP - Mein einfacher Mp3-Player

Kapitel 6. Events

Wenn wir jetzt auf den Play-Button klicken, dann fängt die Wiedergabe an, falls wir zuvor eine Datei ausgewählt haben. Allerdings steht dann auf dem Button weiterhin „Play“, obwohl er dann ja eigentlich die Funktion „Pause“ besitzt. Wir könnten nun im OnClick-Event das Ändern der Caption einfügen. Und wenn wir dann auf Stop klicken, muss die Caption des Play-Buttons wieder zurück auf Play gesetzt werden.

Wenn man den Player später ausbauen möchte, verliert man so schnell den Überblick, und wenn man an einer Stelle vergisst, den Code zu ändern, führt das für den User schnell zu Inkonsistenzen und sehr unschönen Effekten. Und für uns als Programmierer zu einem unglaublichen Chaos.

Wir fügen unserer Player-Klasse daher einige Events hinzu, die bei passender Gelegenheit abgefeuert werden, und auf die wir in unserer Anwendung dann entsprechend reagieren können.

TMeMPPlayer = class
  private
    fOnPlay    : TNotifyEvent;
    fOnPause   : TNotifyEvent;
    fOnResume  : TNotifyEvent;
    fOnStop    : TNotifyEvent;
    fOnChange  : TNotifyEvent;
    fOnEndFile : TNotifyEvent;
    fFileEndSyncHandle: DWord;
    procedure SetEndSync;
  public
    property OnPlay    : TNotifyEvent read fOnPlay     write fOnPlay    ;
    property OnPause   : TNotifyEvent read fOnPause    write fOnPause   ;
    property OnResume  : TNotifyEvent read fOnResume   write fOnResume  ;
    property OnStop    : TNotifyEvent read fOnStop     write fOnStop    ;
    property OnChange  : TNotifyEvent read fOnChange   write fOnChange  ;
    property OnEndFile : TNotifyEvent read fOnEndFile  write fOnEndFile ;
end;

OnPlay, OnPause, ...

Diese Events werden ausgelöst, wenn sich der Status des Players ändert. Unsere Pause-Methode sieht dann z.B. so aus:

procedure TMeMPPlayer.Pause;
begin
  BASS_ChannelPause(fMainStream);
  if assigned(fOnPause) then fOnPause(Self);
end;

Die Methoden Play, Stop und Resume müssen entsprechend erweitert werden.

OnChange

Dieses Event wird zusätzlich zu OnPlay ausgelöst, um z.B. eine Aktualisierung der Titelanzeige vornehmen zu können. Zu diesem Zweck führen wir auch eine neue Property ein, die einfach den Playlistentitel des zugrunde liegenden Audiofiles weiterleitet.

TMeMPPlayer = class
  private
    function GetPlaylistTitel: String;
  public
    property PlaylistTitel: String read GetPlaylistTitel;
end;
 
function TMeMPPlayer.GetPlaylistTitel: String;
begin
  if assigned(fMainAudioFile) then
    result := fMainAudioFile.PlaylistTitel
  else
    result := '-';
end;

OnEndFile

Wir wollen ja später einen Player haben, der nicht nur ein Lied abspielt, sondern im Anschluss daran direkt das nächste. Die bass.dll bietet einem nun die Möglichkeit, bei Ende eines Liedes benachrichtigt zu werden. Dies geschieht über so genannte Synchronizer, die nach Erzeugen des Streams diesem hinzugefügt werden können.

procedure TMeMPPlayer.SetEndSync;
begin
  if Assigned(fOnEndFile) then
    fFileEndSyncHandle := Bass_ChannelSetSync(fMainStream,
                  BASS_SYNC_END, 0, @EndFileProc, Self);
end;

Wir fügen hier unserer Wiedergabe einen Synchronizer vom Typ BASS_SYNC_END hinzu. Wenn es soweit ist, wird die Prozedur EndFileProc ausgeführt, die als Parameter unter anderem das aufrufende Player-Objekt erhält. Diese feuert dann einfach das OnEndFile Event ab:

procedure EndFileProc(handle: HWND; Channel, Data, User: DWord); stdcall;
begin
  TMeMPPlayer(User).OnEndFile(TMeMPPlayer(User));
end;

Die Aufrufkonvention stdcall ist hierbei wichtig. Auf ähnliche Art könnte man auch einen Synchronizer setzen, der uns benachrichtigt, wenn wir fast am Ende des Stückes sind. In so einem Fall könnten wir die laufende Wiedergabe sanft ausblenden, die nächste einblenden und so einen schöneren Übergang zwischen einzelnen Titeln zu erhalten.