Thema: Delphi WAV-Dateien und ID3Tag?

Einzelnen Beitrag anzeigen

Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#10

Re: WAV-Dateien und ID3Tag?

  Alt 13. Aug 2007, 11:06
Hi,
auf den ersten Blick fällt mir auf, dass Du das Einlesen/Suchen nach dem Chunk nocht optimieren könntest. So kommt die Datei, die Du lesen möchtest ja von der Festplatte. So eine Festplatte ist etwas anders aufgebaut als der RAM, man hat nicht ganz so schön einen Wahlfreien Zugriff. Natürlich kannst Du ein beliebiges Datum lesen, aber dazu muss es erst auf der Platte gesucht werden. Insbesondere gehört dazu die Platzierung des Lesekopfes (besser gesagt der Leseköpfe) auf der richtigen Spur und dann muss die Platte auch noch warten, bis das richtige Datum unter dem Kopf ist. Einmal gefunden kann diese dann aber sehr sehr sehr schnell viele Daten lesen und als großen Strom weiterreichen. Es gibt aber an sich auch eine untere Grenze, die Blockgröße des Systems.
Worauf ich eigentlich hinaus möchte, nach dem 'I' byteweise zu suchen ist total ineffizient! Zwar greifen hier schon einige Caching-Mechanismen des OS, es wird eben ohnehin nicht nur ein Byte ausgelesen, aber etwas besser geht es, wenn Du hier einen Lesepuffer verwendest. Wenn Du von 100.000 Byte max. Größe ausgehst, könntest Du diese eigentlich schon komplett in den Speicher laden (sind knappe 98 KByte, die hat man ja doch noch frei). Der Vorteil liegt einfach darin, dass der ganze Overhead (ggf. Zugriff auf den Hintergrundspeicher, auslesen aus dem Stream, Stream.Position aktualisieren, ...) nun auf die 98 KByte aufgeteilt wird. Der Unterschied ist überraschend deutlich und sollte selbst bei Deiner kurzen Suchen (in nur 100.000 max. Schritten) spürbar oder zumindestens messbar sein.
An sich kannst Du aber auch die Suche nach dem 'Info' noch weiter vereinfachen. Das was Du aus dem Stream lädst sind erstmal nur rohe Bytes, die eben interpretiert werden. Ein String funktioniert auf die gleiche Art und Weise. Was Du also machen kannst ist einfach die Daten in einen Stream kopieren. Wichtig dabei, Du musst vorher die Länge des Strings setzen!

Grob geht das wie folgt:
Delphi-Quellcode:
function getInfoChunkPos(const buffer: TByteDynArray): Integer;
var s: String;
    index: Integer;
    count: Integer;
begin
  result := -1;
  index := 0;
  
  // über den ganzen Puffer laufen
  while (index < length(buffer)) and (result < 0) do
  begin
    // max. Länge durch 1024 oder noch nicht gelesene Daten im Puffer beschränken
    count := min(1024, length(buffer) - index);
    // Größe des String setzen
    setLength(s, count);
    // Daten aus dem Puffer in den String kopieren
    move(buffer[index], s[1], count);
    // neuen Startindex im Puffer setzen
    inc(index, count);

    // mit dem String arbeiten
    result := pos('Info', s) - 1;
  end;
end;
Ist jetzt ein einfaches Beispiel, bei dem Du ein beliebiges dyn. Array von Byte übergibst (z.B. aus einer Wave-Datei). In einer Schleife werden immer in 1024-Byte-Happen die Daten aus dem Puffer in einen String kopiert (optimieren kannst Du das natürlich, wenn Du sicherstellst, dass der Puffer ein vielfaches von 1024 ist!). Auf den String wird dann die Pos-Funktion angewendet, die Dir den Index des ersten Vorkommens des Substrings 'Info'. Das zusätzliche -1 bei der letzten Zuweisung kommt daher, dass ein String immer 1 indiziert ist, das erste Element in einem Stream/dyn. Array/... aber immer die 0 bekommt. Somit wäre die Position hier um 1 versetzt.
Ob 1024 hier eine gute Größe ist, darüber lässt sich natürlich streiten. Zudem solltest Du natürlich beachten, dass man den String in zwei Teilen aus dem Puffer holen kann, also ein String mit 'Inf' endet und das erste Zeichen des nächsten Strings dann eben mit 'o' beginnt. Um das zu vermeiden sollte man bessert inc(index, count - 4) verwenden, das sollte sicherstellen, dass das Ende des Vorgängerstrings noch mit berücksichtigt wird.

Ansonsten sieht der Code doch ganz ordentlich aus, ein paar Kleinigkeiten gibt es natürlich dennoch (nichts Wildes!). Zum Beispiel kannst Du bei lokalen Variablen wie dem Stream auf das FreeAndNil verzichten. Wird eine Referenz nach ihrer Freigabe eh nicht mehr erreicht, reicht auch ein Free. Es schadet aber auch nichts zusätzlich die Referenz auf NIL zu setzen. Auch der Typ der Variable Stream kann direkt als TFileStream gesetzt werden. Stream verwendet man eigentlich immer dort, wo es einem völlig egal ist, was für ein Stream übergeben wird. macht auch hier nichts, Du könntest eben nur nicht auf Besonderheiten eines TFileStream (ohne Cast) zugreifen.
Die letzte Kleinigkeit, die noch am ehesten berücksichtigt werden sollte ist die von Dir verwendete Sperre, wenn Du die Dateien veränderst. Beim Lesen ist das fmShareDenyWrite natürlich sinnvoll, da Du so beliebig vielen Prozessen das Lesen ermöglichst. Aber wenn Du selbst die Datei schreibst, dann sollte möglichst kein anderer Prozess versuchen die Datei während dessen zu lesen. Was man sonst zu sehen bekommt kann eben doch irgendwas völlig Inkonsistentes sein. Besser wäre es, wenn Du hier gleich exklusiv (Lesen und Schreiben) sperrst (fmShareExclusive).

Gruß Der Unwissende
  Mit Zitat antworten Zitat