好久没写BLOG了,送上一份原创的DELPHI版MP3切割,splitMp3为切割函数,支持按时间切割和按大小切割。望大家支持。
参考VC的资料编写的MP3切割DELPHI版单元.
unit UnitMp3DataUtil;
{
MP3 Cut Unit.
@author Jim Wu
2009-08
}
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls;
type
Mp3SplitMode = (Mp3SplitByTime, Mp3SplitByDateLength);
TMp3DataInfo = packed record
isMp3: Boolean;
bitrate: Integer;
sampleRate: Integer;
isMono: Boolean;
isVBR: Boolean;
end;
function readMp3Head(fileName: string): TMp3DataInfo;
function splitMp3(mode: Mp3SplitMode; length: Integer; fr: TFileStream; writeFileName: string; var actualLength: Integer; var actualMillisecond: Integer): integer;
const
mpegBitrateTable: array[1..2,1..3,1..14] of Integer=(
(
( 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448),
( 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384),
( 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320)
),
(
( 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256),
( 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160),
( 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160)
)
);
samplingRateTable: array[1..3,0..2] of Integer=(
(44100,48000,32000),
(22050,24000,16000),
(11025,12000,8000)
);
frameLengthTable: array[1..2,1..3] of Integer=(
(48000,144000,144000),
(24000, 72000, 72000)
);
samplesPerFrameTable: array[1..2,1..3] of Integer=(
( 384,1152,1152),
( 384,1152, 576)
);
implementation
function readMp3Head(fileName: string): TMp3DataInfo;
var
binData: array [0..3] of Byte;
tempDate: array [0..3] of Byte;
fr: TFileStream;
mpegPadding: Byte;
mpegVersion: Double;
mpegLayer: Byte;
seekDataLength: Integer;
begin
fr := TFileStream.Create(fileName, fmOpenRead);
fr.Read(binData, 4);
if (binData[0] = $49) and (binData[1] = $44) and (binData[2] = $33) then
begin //readMp3Head
fr.Seek(2, soFromCurrent);
fr.Read(tempDate, 4); //解析长度
seekDataLength := tempDate[0] and $7F shl 21 + tempDate[1] and $7F shl 14 + tempDate[2] and $7F shl 7 + tempDate[3] and $7F;
fr.Seek(seekDataLength, soFromCurrent);
fr.Read(binData, 4);
end;
if binData[0] = $FF then //DATA Frame
begin
case binData[1] shr 3 and $3 of
0: mpegVersion := 2.5;
1: ; //reserved
2: mpegVersion := 2;
3: mpegVersion := 1;
end;
mpegLayer := 4 - (binData[1] shr 1 and $3);
result.bitrate := mpegBitrateTable[Trunc(mpegVersion)][mpegLayer][binData[2] shr 4 and $F];
result.sampleRate := samplingRateTable[Trunc(mpegVersion+0.5)][binData[2] shr 2 and $3];
mpegPadding := binData[2] shr 1 and $1;
if binData[3] shr 6 and $3 = 3 then
result.isMono := True
else
Result.isMono := False;
fr.Seek(32, soFromCurrent); //read Xing(VBR) Flag
fr.Read(tempDate, 4);
if (tempDate[0] = $58) and (tempDate[1] = $69) and (tempDate[2] = $6E) and (tempDate[3] = $67) then
Result.isVBR := True
else
Result.isVBR := False;
Result.isMp3 := True;
end
else
Result.isMp3 := False; //not MP3 file
fr.Free;
end;
function processFrame(fr: TFileStream; fw: TFileStream; var writeLength: Integer; var writeMillisecond: double): LongInt; //返回:文件位置
var
binData: array [0..3] of Byte;
seekDataLength: Integer;
mpegPadding: Byte;
mpegVersion: Double;
mpegLayer: Byte;
mpegBitrate: Integer;
mpegSamplingRate : Integer;
trackMode: Integer;
begin
fr.Read(binData, 4);
if (binData[0] = $49) and (binData[1] = $44) and (binData[2] = $33) then
begin //IDV3
fr.Seek(2, soFromCurrent);
fr.Read(binData, 4); //解析长度
seekDataLength := binData[0] and $7F shl 21 + binData[1] and $7F shl 14 + binData[2] and $7F shl 7 + binData[3] and $7F;
fr.Seek(seekDataLength, soFromCurrent); //instead of write
writeLength := 0;
writeMillisecond := 0;
end
else
if binData[0] = $FF then //DATA Frame
begin
case binData[1] shr 3 and $3 of
0: mpegVersion := 2.5;
1: ; //reserved
2: mpegVersion := 2;
3: mpegVersion := 1;
end;
mpegLayer := 4 - (binData[1] shr 1 and $3);
mpegBitrate := mpegBitrateTable[Trunc(mpegVersion)][mpegLayer][binData[2] shr 4 and $F];
mpegSamplingRate := samplingRateTable[Trunc(mpegVersion+0.5)][binData[2] shr 2 and $3];
mpegPadding := binData[2] shr 1 and $1;
seekDataLength := Trunc((samplesPerFrameTable[Trunc(mpegVersion)][mpegLayer] / 8 * mpegBitrate * 1000) / mpegSamplingRate) + mpegPadding;
fr.Seek(32, soFromCurrent); //read Xing(VBR) Flag
fr.Read(binData, 4);
if (binData[0] = $58) and (binData[1] = $69) and (binData[2] = $6E) and (binData[3] = $67) then
begin
fr.Seek(seekDataLength - 4 - 32 - 4, soFromCurrent);
result := fr.Position;
writeLength := 0;
writeMillisecond := 0;
end
else //writeData
begin
fr.Seek(-32 - 4 - 4, soFromCurrent);
fw.CopyFrom(fr, seekDataLength); //copy a frame
writeLength := seekDataLength;
writeMillisecond := samplesPerFrameTable[Trunc(mpegVersion)][mpegLayer] * 1000.0 / mpegSamplingRate;
result := fr.Position;
end;
end
else
begin
writeLength := 0;
writeMillisecond := 0;
result := fr.Position;
end;
end;
function splitMp3(mode: Mp3SplitMode; length: Integer; fr: TFileStream; writeFileName: string; var actualLength: Integer; var actualMillisecond: Integer): integer;
//length:限制,对于SplitByTime,为毫秒,对于长度,为byte
//actualLength为实际切分长度,actualTime为实际切分毫秒
var
writeLength: Integer;
writeMillisecond: double;
tempMillisecond: double;
fw: TFileStream;
total: double;
begin
fw := TFileStream.Create(writeFileName, fmOpenWrite or fmCreate);
total := 0;
actualLength := 0;
tempMillisecond := 0;
while True do
begin
if fr.Position >= fr.Size - 4 then
break;
if total >= length then
break;
result := processFrame(fr, fw, writeLength, writeMillisecond);
Inc(actualLength, writeLength);
tempMillisecond := tempMillisecond + writeMillisecond;
if mode = Mp3SplitByTime then
total := total + writeMillisecond
else
total := total + writeLength;
end;
actualMillisecond := Trunc(tempMillisecond);
fw.Free;
end;
end.