MeMP - Mein einfacher Mp3-Player

Kapitel 8. Das Plugin-System der bass.dll

Die bass.dll unterstützt von sich aus nur recht wenige Formate. Die gängigsten wie mp3, wav und ogg sind dabei, aber schon bei wma gibt es Probleme – das geht einfach nicht. Allerdings gibt es dafür Zusatz-Dlls, die diese Aufgabe übernehmen können. Es gibt eine für wma, eine für ape, flac, aac und einige weitere.

Zunächst einmal müsste jedes Format über die entsprechende BASS_XXX_StreamCreateFile-Methode gestartet werden, was zu einer ähnlichen Konstruktion wie beim Auslesen der Audio-Informationen führen würde. Das würde aber bedeuten, dass wir als Programmierer alle dlls direkt mitliefern müssen, und auch alle zugehörigen .pas-Dateien in unser Projekt aufnehmen müssen. Und spätestens bei der bass_aac.dll bekommen wir hier Probleme, denn diese steht unter der GPL (und ich gehe jetzt einfach mal davon aus, dass wir unseren Player nicht unbedingt unter dieser Lizenz vertreiben wollen).

An dieser Stelle verweise ich mal auf die Beispiel-Projekte, die bei der bass.dll mitgeliefert werden. Da stecken nämlich eine Menge nützlicher Dinge drin, und man kann sich da einiges abgucken. Jetzt brauchen wir Plugins.

Die bass.dll bietet nämlich ein recht einfaches Plugin-System, um genau diese Probleme zu beheben. Wir können beim Start unseres Players nach gültigen Plugin-Dlls in unserem Programmverzeichnis suchen und diese einbinden. Wir bekommen dabei auch Informationen darüber, was dieses Plugin kann, z.B. welche Dateitypen unterstützt werden. Die bass.dll wiederum sucht sich dann beim Abspielen einer Datei automatisch das passende Plugin heraus, und nimmt uns so eine Menge Arbeit ab.

Der User hat davon den Vorteil, dass er sich bei Bedarf die passende Dll besorgen kann, um mit unserem Player auch das von ihm bevorzugte etwas exotischere Format abspielen zu können.

Wir erweitern die Player-Klasse also noch ein wenig.

TMeMPPlayer = class
  private
    fFilter: String;
    fValidExtensions: Tstringlist;
  public
    property Filter: String read fFilter;
    procedure InitPlugins(PathToDlls: String);
    function IsPlayableFile(aFilename: String): Boolean;
end;

Die Eigenschaft Filter können wir später bei einem OpenDialog einsetzen, die Funktion IsPlayableFile sagt uns, ob die bass.dll mit einer Datei, die so eine Endung besitzt, etwas anfangen kann. Dies können wir später z.B. bei einem OpenDirectoryDialog anwenden, wenn wir den ausgewählten Ordner (rekursiv) nach verwertbaren Musikdateien durchsuchen, oder per Drag&Drop einige Dokumente auf die Form ziehen. Der Filter und die Liste der gültigen Formate werden in der Prozedur InitPlugins ermittelt.

procedure TMeMPPlayer.InitPlugins(PathToDlls: String);
var fh: THandle;
    fd: TWin32FindData;
    Plug: DWORD;
    Info: PBass_PluginInfo;
    a,i: integer;
    tmpext: TStringlist;
begin
  PathToDlls := IncludeTrailingPathDelimiter(PathToDlls);
  fFilter := '|Standardformate (*.mp3;*.mp2;*.mp1;*.ogg;*.wav;*.aif)'
             + '|*.mp3;*.mp2;*.mp1;*.ogg;*.wav*;*.aif';
  // Plugins laden. Code aus dem Plugin-Beispiel-Projekt
  fh := FindFirstFile(PChar(PathToDlls + 'bass*.dll'), fd);
  if (fh <> INVALID_HANDLE_VALUE) then
  try
    repeat
      Plug := BASS_PluginLoad(fd.cFileName, 0);
      if Plug <> 0 then
      begin
        // Plugin-Info ermitteln
        Info := BASS_PluginGetInfo(Plug);
        for a := 0 to Info.formatc - 1 do
        begin
          // Filter erweitern
          fFilter := fFilter + '|' + Info.Formats[a].name + ' ('
                     + Info.Formats[a].exts + ')|' + Info.Formats[a].exts;
          //einzelne Erweiterungen ermitteln
          tmpext := Explode(';',Info.Formats[a].exts);
          for i := 0 to tmpext.Count - 1 do
            fValidExtensions.Add(StringReplace(tmpext.Strings[i],'*', '',[]));
          FreeAndNil(tmpext);
        end;
      end;
    until FindNextFile(fh, fd) = false;
  finally
    Windows.FindClose(fh);
  end;
end;

Diese Prozedur ist etwas länger, aber prinzipiell einfach. Das übergebene Verzeichnis wird nach Dateien durchsucht, die ein Plugin für die bass.dll sein könnten. Die Funktion BASS_PluginInfo liefert diverse Informationen zurück, die wir in den Filter und die Liste der gültigen Datenamenserweiterungen eintragen.

Die zusätzliche Funktion IsPlayableFile überprüft später einfach, ob der übergebene Dateiname eine Erweiterung besitzt, die in dieser Liste vorkommt.

function TMeMPPlayer.IsPlayableFile(aFilename: String): Boolean;
begin
  result := fValidExtensions.IndexOf(ExtractFileExt(aFilename)) >= 0;
end;

Da die Initialisierungsfunktion nur die Erweiterungen aus den Plugins bestimmt, sollten wir im Konstruktor des Players die Standardformate einfügen.