Das hat mir keine Ruhe gelassen, und daher hab ich mich mal ein Stündchen hingesetzt, und meine MPEG-Klasse etwas erweitert.
Das ganze ist eher ein Proof-Of-Concept, aber es sollte soweit funktionieren. Bei VBR gibts die Einschränkung, dass ein evtl. vorhandenes optionales Sprungstellen-Array gelöscht wird. Die Anzahl der Frames in diesem Xing-Header wird aber korrigiert. Das Array anzupassen dürfte auch kein prinzipielles Problem sein, allerdings verstehe ich nicht ganz, was da für Daten drin stehen.
Folgende Funktion bröselt eine MP3-Datei in die einzelnen Frames auf:
Delphi-Quellcode:
function TMpegInfo.ReadFrames(stream: TStream): Integer;
var c, i, bufpos: Integer;
buffer: TBuffer;
aMpegHeader: TMpegHeader;
begin
LoadFromStream(stream);
Setlength(fFrameArray, fFramesCount);
setlength(buffer, stream.Size);
Stream.Seek(0, soFromBeginning);
c := Stream.Read(buffer[0], length(buffer));
if c < Stream.Size then Setlength(Buffer, c);
bufpos := fFirstHeaderPosition;
fFramesread := 0;
// besser wäre eigentlich: Die forschleife ersetzen durch eine,
// die bis zum Dateiende läuft
for i := 0 to fFramesCount-1 do
begin
aMpegHeader := GetValidatedHeader(buffer, bufpos);
if aMpegHeader.valid then
begin
Setlength(fFrameArray[i], aMpegHeader.framelength);
Move(buffer[bufpos], fFrameArray[i][0], aMpegHeader.framelength);
bufpos := bufpos + aMpegHeader.framelength;
inc(fFramesread);
end else
begin
// d.h. es wurde kein gültiger Frame mehr gefunden
// d.h. irgendwas ist falsch -einfach den Rest kopieren
// mögliche Ursache, falls man den oben erwähnten besseren Weg wählt:
// Ende der Musik, und Start von ID3v1-Tag oder Lyrics-Tag.
// Das könnte man ggf. abfangen.
Setlength(fFrameArray[i], length(buffer) - bufpos);
Move(buffer[bufpos], fFrameArray[i][0], length(fFrameArray[i]));
bufpos := bufpos + aMpegHeader.framelength;
inc(fFramesread);
result := fFramesread;
exit;
end;
end;
result := fFramesRead;
end;
...und diese schreibt einen Teil in einen (neuen) Stream. Erwartete start-ende-Werte sind in Millisekunden anzugeben.
Delphi-Quellcode:
// leicht gekürzt
function TMpegInfo.Cut(stream: TStream; start,ende: LongInt): Boolean;
var idxstart, minidx, idxend, i: integer;
begin
result := False;
if FirstFrameIsXing then
minidx := 1
else
minidx := 0;
//...
// Aus den zeiten den Start- und EndIndex im Frame-Array berechnen
// ein MPEGFrame ist 26ms lang
idxstart := Round(start Div 26);
idxend := Round(ende DIV 26);
//...
if vbr and CorrectXingHeader(idxend - idxstart + 1) then
Stream.WriteBuffer(fFrameArray[0][0], length(fFrameArray[0]));
for i := idxstart to idxend do
begin
Stream.WriteBuffer(fFrameArray[i][0], length(fFrameArray[i]));
end;
result := True;
end;