summaryrefslogtreecommitdiff
path: root/plugins/Watrack/formats/fmt_mp3.pas
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Watrack/formats/fmt_mp3.pas')
-rw-r--r--plugins/Watrack/formats/fmt_mp3.pas460
1 files changed, 460 insertions, 0 deletions
diff --git a/plugins/Watrack/formats/fmt_mp3.pas b/plugins/Watrack/formats/fmt_mp3.pas
new file mode 100644
index 0000000000..5d6c94745a
--- /dev/null
+++ b/plugins/Watrack/formats/fmt_mp3.pas
@@ -0,0 +1,460 @@
+{MP3 file process}
+unit fmt_MP3;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadMP3(var Info:tSongInfo):boolean; cdecl;
+function ReadMPG(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format;
+
+const
+ ScanSize = 16*1024; // block size to search header
+type
+ tMP3FrameHdr = record
+ Version :integer;
+ Layer :cardinal;
+ Bitrate :cardinal;
+ Samplerate:cardinal;
+ Channel :cardinal; //Stereo, Joint, Dual, Mono
+ Length :cardinal;
+ CRC :boolean;
+ _Private :boolean;
+ Copyright :boolean;
+ Original :boolean;
+ isVBR :boolean;
+ end;
+
+// ........ ........ 111..... 11111111 syncword
+// ........ ........ ...xx... ........ version (11=1, 10=2, 00=2.5)
+// ........ ........ .....xx. ........ layer (01=III, 10=II, 11=I)
+// ........ ........ .......x ........ crc (0=yes, 1=no)
+// xx...... ........ ........ ........ mode (00=stereo, 10=joint, 01=dual, 11=mono)
+// ..xx.... ........ ........ ........ mode ext (only for joint stereo)
+// ....x... ........ ........ ........ copyright (0=no, 1=yes)
+// .....x.. ........ ........ ........ original (0=orig, 1=copy)
+// ......xx ........ ........ ........ emphasis (not 10)
+// ........ xxxx.... ........ ........ bitrate (not 0000 nor 1111)
+// ........ ....xx.. ........ ........ sampling rate (not 11)
+// ........ ......x. ........ ........ padded (0=no, 1=yes)
+// ........ .......x ........ ........ private bit
+
+const
+ btable:array [0..1,0..2,0..15] of word = (
+ ( //MPEG 2 & 2.5
+ (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0), //Layer III
+ (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0), //Layer II
+ (0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0) //Layer I
+ ),( //MPEG 1
+ (0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,0), //Layer III
+ (0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384,0), //Layer II
+ (0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448,0) //Layer I
+ )
+ );
+ stable: array [0..3,0..2] of word = (
+ (32000, 16000, 8000), //MPEG 2.5
+ ( 0, 0, 0), //reserved
+ (22050, 24000, 16000), //MPEG 2
+ (44100, 48000, 32000) //MPEG 1
+ );
+
+procedure TranslateFrameHdr(const block:array of byte;var MP3FrameHdr:tMP3FrameHdr);
+begin
+ FillChar(MP3FrameHdr,SizeOf(MP3FrameHdr),0);
+ if block[0]=$FF then
+ begin
+ with MP3FrameHdr do
+ begin
+ Version :=(block[1] and $18) shr 3;
+ Layer :=(block[1] and $06) shr 1;
+ CRC :=not Odd(block[1]);
+ Bitrate :=btable[Version and 1][Layer-1][block[2] shr 4];
+ Samplerate:=stable[Version][(block[2] and $0C) shr 2];
+ _Private :=odd(block[2]);
+ Channel :=block[3] shr 6;
+ Copyright :=((block[3] and $08) shr 3)<>0;
+ Original :=((block[3] and $04) shr 2)<>0;
+ end;
+ end;
+end;
+
+procedure CheckVBR(f:THANDLE; var hdr:tMP3FrameHdr);
+var
+ pos,apos:cardinal;
+ sign:longint;
+ frames:longint;
+begin
+ pos:=FilePos(f);
+ hdr.Length:=0;
+ if hdr.Version=3 then
+ begin
+ if hdr.Channel=3 then
+ apos:=17
+ else
+ apos:=32;
+ end
+ else if hdr.Channel=3 then
+ apos:=9
+ else
+ apos:=17;
+ Skip(f,apos);
+ BlockRead(f,sign,4);
+ hdr.isVBR:=sign=$676E6958; //Xing
+//calculate length
+ if hdr.isVBR then
+ begin
+ if hdr.Samplerate<>0 then
+ begin
+// Seek(f,pos+36);
+ BlockRead(f,sign,4);
+ if (sign and $01000000)<>0 then
+ begin
+ BlockRead(f,frames,4);
+ frames:=BSwap(frames);
+ hdr.Length:=Round((1152/hdr.Samplerate)*frames/(4-hdr.Version)); //!
+ end;
+ end;
+ end
+ else if hdr.Bitrate<>0 then
+ hdr.Length:=((8*(FileSize(f)-(pos-4))) div 1000) div hdr.Bitrate;
+end;
+
+function SearchStart(f:THANDLE; var l:array of byte):Boolean;
+var
+ CurPos:longint;
+ Buf:array [0..ScanSize] of byte;
+ i,j:integer;
+begin
+ CurPos:=FilePos(f)-4;
+ Seek(f,CurPos);
+ j:=BlockRead(f,Buf,ScanSize);
+ i:=0;
+ while i<j do
+ begin
+ if (i<(j-2)) and (Buf[i]=$FF) and //FF FB E4
+ ((Buf[i+1] and $E0)=$E0) and
+ ((Buf[i+2] and $F0)<>$F0) then
+ begin
+ Seek(f,CurPos+i);
+ BlockRead(f,l,4);
+ result:=true;
+ Exit;
+ end;
+ inc(i);
+ end;
+ result:=false;
+end;
+
+function ReadMP3(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ l:array [0..3] of byte;
+ hdr:tMP3FrameHdr;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,l,4);
+// if l[0]<>$FF then
+ if not SearchStart(f,l) then
+ Exit;
+ TranslateFrameHdr(l,hdr);
+ CheckVBR(f,hdr);
+ Info.kbps :=hdr.Bitrate;
+ Info.khz :=hdr.Samplerate div 1000;
+ Info.total:=hdr.Length;
+ if hdr.Channel=3 then
+ Info.channels:=1
+ else
+ Info.channels:=2;
+ Info.vbr:=ord(hdr.isVBR);
+
+ ReadAPEv2(f,Info);
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+const
+ mpgAudio = 1;
+ mpgVideo = 2;
+ mpgVersion = 4;
+
+type
+ l2b=array [0..3] of byte;
+
+function ReadDWord(var p:pAnsiChar;endptr:pAnsiChar):integer;
+begin
+ if (p+4)<endptr then
+ begin
+ result:=pDWord(p)^;
+ inc(p,4);
+ end
+ else
+ result:=-1;
+end;
+
+function ReadWord(var p:pAnsiChar;endptr:pAnsiChar):integer;
+begin
+ if (p+2)<endptr then
+ begin
+ result:=pWord(p)^;
+ inc(p,2);
+ end
+ else
+ result:=-1;
+end;
+
+function ReadByte(var p:pAnsiChar;endptr:pAnsiChar):integer;
+begin
+ if p<endptr then
+ begin
+ result:=pByte(p)^;
+ inc(p);
+ end
+ else
+ result:=-1;
+end;
+
+function ChunkRead(var p:pAnsiChar;endptr:pAnsiChar):integer;
+var
+ i:integer;
+begin
+ repeat
+ if ReadByte(p,endptr)=0 then
+ if ReadByte(p,endptr)=0 then
+ begin
+ repeat
+ i:=ReadByte(p,endptr);
+ until i<>0;
+ if i=1 then
+ begin
+ result:=ReadByte(p,endptr) or $100;
+ exit;
+ end;
+ end;
+ until p>=endptr;
+ result:=0;
+end;
+
+const
+ BufSize = 256*1024;
+
+function ReadMPG(var Info:tSongInfo):boolean; cdecl;
+var
+ endptr,buf,p:PAnsiChar;
+ f:THANDLE;
+ BlockType:integer;
+ l:dword;
+ w:word;
+ b:byte;
+ flag:integer;
+ version,layer:integer;
+// vbitrate:integer;
+// FrmCnt:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ flag:=mpgAudio+mpgVideo+mpgVersion;
+
+ mGetMem(buf,BufSize);
+ endptr:=buf+BlockRead(f,buf^,BufSize);
+ CloseHandle(f);
+ p:=buf;
+// FrmCnt:=0;
+ while (flag<>0) and (p<endptr) do
+ begin
+ BlockType:=ChunkRead(p,endptr);
+ case BlockType of
+ $1BA: begin // pack
+ if (flag and mpgVersion)<>0 then
+ begin
+ flag:=flag and not mpgVersion;
+ if (ReadByte(p,endptr) and $C0)=$40 then
+ Info.codec:=$3247504D // MPG2
+ else
+ Info.codec:=$4745504D; // MPEG
+ end;
+ end;
+ $1B3: begin // Video
+ if (flag and mpgVideo)<>0 then
+ begin
+ l:=ReadDWord(p,endptr);
+ flag:=flag and not mpgVideo;
+ Info.width :=((l2b(l)[1] and $F0) shr 4)+(l2b(l)[0] shl 4);
+ Info.height:=((l2b(l)[1] and $0F) shl 8)+l2b(l)[2];
+ case l2b(l)[3] and $F of
+ 1: Info.fps:=2397;
+ 2: Info.fps:=2400;
+ 3: Info.fps:=2500;
+ 4: Info.fps:=2997;
+ 5: Info.fps:=3000;
+ 6: Info.fps:=5000;
+ 7: Info.fps:=5994;
+ 8: Info.fps:=6000;
+ end;
+// BlockRead(f,l,4);
+// vbitrate:=(l2b(l)[0] shl 10)+(l2b(l)[1] shl 2)+(l2b(l)[2] shr 6);
+ end;
+ end;
+ 0,$1B7,$1B9: break;
+{
+ $1E0: begin
+ BlockRead(f,w,2);
+ w:=swap(w);
+ mGetMem(buf,w);
+ BlockRead(f,buf^,w);
+ p:=buf;
+ for l:=0 to w-4 do
+ begin
+ if pdword(p)^=$00010000 then
+ begin
+ inc(FrmCnt);
+ inc(p,4);
+ end
+ else
+ inc(p);
+ end;
+ mFreeMem(buf);
+ end;
+}
+ $1C0: begin // audio
+ w:=swap(ReadWord(p,endptr));
+ if flag and mpgAudio<>0 then
+ begin
+ flag:=flag and not mpgAudio;
+ b:=ReadByte(p,endptr);
+ dec(w);
+ if (b and $C0)=$80 then
+ begin
+ b:=ReadByte(p,endptr);
+ l:=ReadByte(p,endptr);
+ dec(w,2);
+ if (b and $80)<>0 then
+ begin
+ inc(p,5);
+ dec(w,5);
+ dec(l,5);
+ if (b and $40)<>0 then
+ begin
+ inc(p,5);
+ dec(w,5);
+ dec(l,5);
+ end;
+ end;
+ if l>0 then
+ begin
+ inc(p,l);
+ dec(w,l);
+ end;
+ end
+ else
+ begin
+ while (b and $80)<>0 do
+ begin
+ dec(w);
+ if w=0 then break;
+ b:=ReadByte(p,endptr);
+ end;
+ if (b and $40)<>0 then
+ begin
+ inc(p);
+ b:=ReadByte(p,endptr);
+ dec(w,2);
+ end;
+ if (b and $20)<>0 then
+ begin
+ inc(p,4);
+ dec(w,4);
+ if (b and $10)<>0 then
+ begin
+ inc(p,5);
+ dec(w,5);
+ end;
+ end;
+ end;
+ l:=ReadDWord(p,endptr);
+ version:=(l2b(l)[1] and $18) shr 3;
+ layer :=(l2b(l)[1] and $06) shr 1;
+ Info.kbps :=btable[version and 1][layer-1][l2b(l)[2] shr 4];
+ Info.khz :=(stable[version][(l2b(l)[2] and $0C) shr 2]) div 1000;
+ Info.channels:=l2b(l)[3] shr 6;
+ if Info.channels=3 then
+ Info.channels:=1
+ else
+ Info.channels:=2;
+// if w>0 then inc(p,w);
+ end;
+// else
+ inc(p,w);
+ end;
+{
+ $1B5: begin
+ BlockRead(f,l,4);
+ if (l2b(l)[0] and $F0)=$10 then
+ begin
+ vbitrate:=vbitrate+
+ ((((l2b(l)[2] and $1F) shl 7)+(l2b(l)[3] shr 1)) shl 18);
+ end;
+ end;
+}
+{
+ $1BD: begin
+ end;
+}
+ $1C1..$1DF, // audio
+//?? $1E0,
+ $1E1..$1EF, // video
+ $1BB{,$1BD},$1BE,$1BF: begin // system,private,padding,private
+ inc(p,swap(ReadWord(p,endptr)));
+ end;
+ end;
+ end;
+// vbitrate:=(vbitrate*400) div 1000;
+// Info.total:=(FrmCnt*100) div Info.fps;
+ mFreeMem(buf);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkMP3,
+ LocalFormatLinkMPG,
+ LocalFormatLinkMPEG:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkMP3.Next:=FormatLink;
+
+ LocalFormatLinkMP3.This.proc :=@ReadMP3;
+ LocalFormatLinkMP3.This.ext :='MP3';
+ LocalFormatLinkMP3.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkMP3;
+
+ LocalFormatLinkMPG.Next:=FormatLink;
+
+ LocalFormatLinkMPG.This.proc :=@ReadMPG;
+ LocalFormatLinkMPG.This.ext :='MPG';
+ LocalFormatLinkMPG.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkMPG;
+
+ LocalFormatLinkMPEG.Next:=FormatLink;
+
+ LocalFormatLinkMPEG.This.proc :=@ReadMPG;
+ LocalFormatLinkMPEG.This.ext :='MPEG';
+ LocalFormatLinkMPEG.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkMPEG;
+end;
+
+initialization
+ InitLink;
+end.