diff options
author | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-10-08 18:43:29 +0000 |
---|---|---|
committer | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-10-08 18:43:29 +0000 |
commit | 864081102a5f252415f41950b3039a896b4ae9c5 (patch) | |
tree | c6b764651e9dd1f8f53b98eab05f16ba4a492a79 /plugins/Watrack/formats/fmt_ogg.pas | |
parent | db5149b48346c417e18add5702a9dfe7f6e28dd0 (diff) |
Awkwars's plugins - welcome to our trunk
git-svn-id: http://svn.miranda-ng.org/main/trunk@1822 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/Watrack/formats/fmt_ogg.pas')
-rw-r--r-- | plugins/Watrack/formats/fmt_ogg.pas | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/plugins/Watrack/formats/fmt_ogg.pas b/plugins/Watrack/formats/fmt_ogg.pas new file mode 100644 index 0000000000..4b05b80c2c --- /dev/null +++ b/plugins/Watrack/formats/fmt_ogg.pas @@ -0,0 +1,522 @@ +{OGG, SPX and FLAC file formats}
+unit fmt_OGG;
+{$include compilers.inc}
+
+interface
+uses wat_api;
+
+function ReadOGG(var Info:tSongInfo):boolean; cdecl;
+function ReadSPX(var Info:tSongInfo):boolean; cdecl;
+function ReadfLaC(var Info:tSongInfo):boolean; cdecl;
+
+implementation
+uses windows,common,io,tags,srv_format,base64,utils;
+
+const
+ OGGSign = $5367674F; //OggS
+const
+ SpeexID = 'Speex ';
+type
+ tSPEXHeader = packed record
+ speex_string :array [0..7] of AnsiChar;
+ speex_version :array [0..19] of AnsiChar;
+ speex_version_id:dword;
+ header_size :dword; //sizeof(tSPEXHeader)
+ rate :dword;
+ mode :dword;
+ bitstrm_version :dword;
+ nb_channels :dword;
+ bitrate :dword;
+ frame_size :dword;
+ vbr :dword;
+ fpp :dword; //frames_per_packet
+ extra_headers :dword;
+ reserved1 :dword;
+ reserved2 :dword;
+ end;
+type
+ pOGGHdr = ^tOGGHdr;
+ tOGGHdr = packed record
+ ID :dword;
+ Version :byte;
+ HdrType :byte;
+ Granule :Int64; // absolute position
+ BitStrmSN:dword;
+ PageSeqN :dword;
+ CRC :dword;
+ PageSegs :byte;
+ end;
+const
+ strmOGG = 1;
+ strmOGM = 2;
+const
+ VideoD = $65646976;
+ VideoW = $006F;
+ VorbisD = $62726F76;
+ VorbisW = $7369;
+type
+ tOGMInfo = packed record
+ padding :word; // 0
+ codec :dword;
+ size :dword;
+ time_unit :int64; // 1/10000000 sec
+ samples_per_unit:int64; // fps = 10000000*spu/time_unit
+ default_len :dword; // 1
+ buffersize :dword;
+ bit_per_sample :dword;
+ width :dword;
+ height :dword;
+ dummy :dword; // 0
+ end;
+
+//const VorbisStream:array [0..5] of byte = ($76,$6F,$72,$62,$69,$73); // 'vorbis'
+
+type
+ tOGGInfo = packed record
+ version :dword;
+ Channels :byte;
+ samplerate:dword;
+ maxkbps :dword;
+ nominal :dword;
+ minkbps :dword;
+ BlockSizes:byte;
+ dummy :byte;
+ end;
+
+//--------------- fLaC section ---------------
+const
+ fLaCSign = $43614C66; //fLaC
+{
+0 : STREAMINFO
+1 : PADDING
+2 : APPLICATION
+3 : SEEKTABLE
+4 : VORBIS_COMMENT
+5 : CUESHEET
+}
+type
+ MetaHdr = packed record
+ blocktype:byte;
+ blocklen:array [0..2] of byte;
+ end;
+type
+ StreamInfo = packed record
+ MinBlockSize:word;
+ MaxBlocksize:word;
+ MinFrameSize:array [0..2] of byte;
+ MaxFrameSize:array [0..2] of byte;
+ heap:array [0..7] of byte;
+ MD5:array [0..15] of byte;
+ end;
+
+procedure OGGGetComment(ptr:PAnsiChar;size:integer;var Info:tSongInfo);
+var
+ clen,alen,len,values:dword;
+ ls:PAnsiChar;
+ value:PAnsiChar;
+ cover:pByte;
+ ext:dword;
+ extw:int64;
+ c:AnsiChar;
+begin
+ inc(ptr,pdword(ptr)^+4); //vendor
+ values:=pdword(ptr)^; inc(ptr,4);
+ ext:=0;
+ cover:=nil;
+ clen:=0;
+ while values>0 do
+ begin
+ len:=pdword(ptr)^;
+ if len>cardinal(size) then
+ break;
+ dec(size,len);
+ inc(ptr,4);
+ ls:=ptr;
+ c:=ls[len];
+ ls[len]:=#0;
+ alen:=StrScan(ls,'=')-ls+1;
+ if alen>0 then
+ begin
+ ls[alen-1]:=#0;
+ value:=ls+alen;
+
+ if (Info.title =nil) and (lstrcmpia(ls,'TITLE' )=0) then UTF8ToWide(value,Info.title)
+ else if (Info.artist =nil) and (lstrcmpia(ls,'ARTIST' )=0) then UTF8ToWide(value,Info.artist)
+ else if (Info.album =nil) and (lstrcmpia(ls,'ALBUM' )=0) then UTF8ToWide(value,Info.album)
+ else if (Info.genre =nil) and (lstrcmpia(ls,'GENRE' )=0) then UTF8ToWide(value,Info.genre)
+ else if (Info.year =nil) and (lstrcmpia(ls,'DATE' )=0) then UTF8ToWide(value,Info.year)
+ else if (Info.comment=nil) and (lstrcmpia(ls,'COMMENT')=0) then UTF8ToWide(value,Info.comment)
+ else if (Info.lyric =nil) and (lstrcmpia(ls,'LYRICS' )=0) then UTF8ToWide(value,Info.lyric)
+
+ else if (Info.track=0) and (lstrcmpia(ls,'TRACKNUMBER')=0) then Info.track:=StrToInt(value)
+
+ else if (cover=nil) and (lstrcmpia(ls,'COVERART')=0) then clen:=Base64Decode(value,cover)
+ else if lstrcmpia(ls,'COVERARTMIME')=0 then ext:=GetImageType(nil,value);
+ end;
+ dec(values);
+ inc(ptr,len);
+ ptr^:=c;
+ end;
+
+ if cover<>nil then
+ begin
+ if ext=0 then
+ ext:=GetImageType(cover);
+ if ext<>0 then
+ begin
+ FastAnsiToWideBuf(PAnsiChar(@ext),pWideChar(@extw));
+ Info.cover:=SaveTemporaryW(cover,clen,PWideChar(@extw));
+ end;
+ mFreeMem(cover);
+ end;
+
+end;
+
+function CalcSize(num:integer;var arr:array of byte):integer;
+var
+ i:integer;
+begin
+ result:=0;
+ for i:=0 to num-1 do
+ begin
+ inc(result,arr[i]);
+ if arr[i]<$FF then break;
+ end;
+end;
+
+function ReadSPX(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ OGGHdr:tOGGHdr;
+ SPXHdr:tSPEXHeader;
+ buf:array [0..255] of byte;
+ ptr:PAnsiChar;
+ size:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ BlockRead(f,OGGHdr,SizeOf(tOGGHdr));
+ Skip(f,OGGHdr.PageSegs);
+ if OGGHdr.ID=OGGSign then
+ begin
+ BlockRead(f,SPXHdr,SizeOf(SPXHdr));
+ if SPXHdr.speex_string<>SpeexID then
+ begin
+ CloseHandle(f);
+ exit;
+ end;
+ Info.khz:=SPXHdr.rate div 1000;
+ Info.vbr:=SPXHdr.vbr;
+ if integer(SPXHdr.bitrate)<>-1 then
+ Info.kbps:=SPXHdr.bitrate div 1000;
+
+ BlockRead(f,OGGHdr,SizeOf(tOGGHdr));
+ BlockRead(f,buf,OGGHdr.PageSegs);
+ size:=CalcSize(OGGHdr.PageSegs,buf);
+ GetMem(ptr,size+1);
+ BlockRead(f,ptr^,size);
+ OGGGetComment(ptr,size,Info);
+ FreeMem(ptr);
+
+ result:=true;
+ end;
+ CloseHandle(f);
+end;
+
+function Compare(const sign:array of byte):integer;
+type
+ conv=packed record
+ d:dword;w:word;
+ end;
+var
+ p:^conv;
+begin
+ p:=@sign;
+ if (p^.d=VideoD) and (p^.w=VideoW) then
+ result:=strmOGM
+ else if (p^.d=VorbisD) and (p^.w=VorbisW) then
+ result:=strmOGG
+ else
+ result:=0;
+end;
+
+function ReadOGG(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ OGGHdr:tOGGHdr;
+ tmp:packed record
+ paktype:byte;
+ sign:array [0..5] of byte;
+ end;
+ OGGInfo:tOGGInfo;
+ OGMInfo:tOGMInfo;
+ fpos:dword;
+ SPXHdr:tSPEXHeader;
+ i,j:integer;
+ DataIndex:integer;
+ buf:array [0..255] of byte;
+ fsize:dword;
+ done:integer;
+ ptr:PAnsiChar;
+ size:integer;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ tmp.paktype:=0;
+ fsize:=FileSize(f);
+ done:=0;
+ while (done<>3) and (tmp.paktype<>5) and (FilePos(f)<fsize) do
+ begin
+ BlockRead(f,OGGHdr,SizeOf(tOGGHdr));
+ if OGGHdr.ID=OGGSign then
+ begin
+ BlockRead(f,buf,OGGHdr.PageSegs);
+ size:=CalcSize(OGGHdr.PageSegs,buf);
+// sum pages to size obtain and number of groups
+// for i:=0 to OGGHdr.PageSegs-1 do
+// only first fragment
+ begin
+ fpos:=FilePos(f);
+ BlockRead(f,tmp,SizeOf(tmp));
+ if tmp.paktype=5 then
+ break;
+ if tmp.paktype=1 then
+ begin
+ case Compare(tmp.sign) of
+ strmOGG: begin
+ BlockRead(f,OGGInfo,SizeOf(OGGInfo));
+ if integer(OGGInfo.nominal)>0 then
+ Info.kbps :=OGGInfo.nominal div 1000;
+ Info.khz :=OGGInfo.samplerate;
+ Info.channels:=OGGInfo.Channels;
+ done:=done or 1;
+ end;
+ strmOGM: begin
+ BlockRead(f,OGMInfo,SizeOf(OGMInfo));
+ Info.codec :=OGMInfo.codec;
+ Info.fps :=round(((10000000*OGMInfo.samples_per_unit) / OGMInfo.time_unit)*100);
+ Info.width :=OGMInfo.width;
+ Info.height:=OGMInfo.height;
+ done:=done or 1;
+ end;
+ end;
+ end
+ else if tmp.paktype=ORD('S') then //maybe SPX
+ begin
+ Seek(f,fpos);
+ BlockRead(f,SPXHdr,SizeOf(SPXHdr));
+ if SPXHdr.speex_string<>SpeexID then
+ begin
+ CloseHandle(f);
+ exit;
+ end;
+ Info.khz:=SPXHdr.rate div 1000;
+ if integer(SPXHdr.bitrate)<>-1 then
+ Info.kbps:=SPXHdr.bitrate div 1000;
+ done:=done or 1;
+ end
+ else if tmp.paktype=3 then
+ begin
+ GetMem(ptr,size+1);
+ BlockRead(f,ptr^,size);
+ OGGGetComment(ptr,size,Info);
+ FreeMem(ptr);
+ done:=done or 2;
+ end
+ else
+ continue;
+ result:=true;
+ end;
+ end;
+ end;
+ // try to get length
+ DataIndex:=FileSize(f)-10;
+ for i:=1 to 50 do
+ begin
+ dec(DataIndex,SizeOf(buf)-10);
+ Seek(f,DataIndex);
+ BlockRead(f,buf,SizeOf(buf));
+ { Get number of PCM samples from last Ogg packet header }
+ j:=SizeOf(buf)-10;
+ repeat
+ if pOGGHdr(@buf[j])^.ID=OGGSign then
+ begin
+ if j>(SizeOf(buf)-SizeOf(tOGGHdr)) then
+ begin
+ Seek(f,DataIndex+j);
+ BlockRead(f,buf,SizeOf(tOGGHdr));
+ j:=0;
+ end;
+ if Info.fps>0 then
+ begin
+ Info.total:=(pOGGHdr(@buf[j])^.Granule*100) div Info.fps;
+ end
+ else if Info.khz<>0 then
+ Info.total:=pOGGHdr(@buf[j])^.Granule div Info.khz;
+ break;
+ end;
+ dec(j);
+ until j=0;
+ if Info.total>0 then break;
+ end;
+ Info.khz:=Info.khz div 1000;
+ CloseHandle(f);
+end;
+
+function ReadfLaC(var Info:tSongInfo):boolean; cdecl;
+var
+ f:THANDLE;
+ data64:int64;
+ hdr:MetaHdr;
+ frm:StreamInfo;
+ id:dword;
+ flag:integer;
+ size:dword;
+ buf,ptr:PAnsiChar;
+begin
+ result:=false;
+ f:=Reset(Info.mfile);
+ if f=THANDLE(INVALID_HANDLE_VALUE) then
+ exit;
+ ReadID3v2(f,Info);
+ BlockRead(f,id,SizeOf(id));
+ if id=fLaCSign then
+ begin
+ flag:=0;
+ repeat
+ BlockRead(f,hdr,SizeOf(hdr));
+ size:=hdr.blocklen[2]+(hdr.blocklen[1] shl 8)+(hdr.blocklen[0] shl 16);
+ case (hdr.blocktype and $7F) of
+ 0: begin
+ if flag=0 then
+ begin
+ BlockRead(f,frm,SizeOf(frm));
+ //samplerate eg.44100
+ Info.khz:=((frm.heap[0] shl 12)+(frm.heap[1] shl 4)+(frm.heap[2] shr 4));
+ Info.channels:=((frm.heap[2] and $F) shr 1)+1;
+ //bits per SAMPLE now
+ Info.kbps:=(frm.heap[2] and 1) shl 4+(frm.heap[3] shr 4)+1;
+ data64:=((frm.heap[3] and $F) shl 32)+(frm.heap[4] shl 24)+
+ (frm.heap[5] shl 16)+(frm.heap[6] shl 8)+frm.heap[7];
+
+ if (data64<>0) and (Info.khz<>0) then
+ Info.total:=data64 div Info.khz;
+ Info.kbps:=Info.kbps*8;
+Info.kbps:=trunc(FileSize(f)*8/1000);
+ Info.khz:=Info.khz div 1000;
+ flag:=1;
+ end;
+ end;
+ 4: begin
+ GetMem(buf,size);
+ BlockRead(f,buf^,size);
+ OGGGetComment(buf,size,Info);
+ FreeMem(buf);
+ end;
+ 6: begin
+ if Info.cover=nil then
+ begin
+ GetMem(buf,size);
+ BlockRead(f,buf^,size);
+ ptr:=buf;
+ id:=BSwap(pdword(ptr)^);
+ case id of
+ 0,3,4,6: begin
+ inc(ptr,4);
+ id:=BSwap(pdword(ptr)^); // mime size
+ inc(ptr,4);
+ flag:=GetImageType(nil,ptr);
+ inc(ptr,id+4*5); // width, height, depth etc.
+ id:=BSwap(pdword(ptr)^); // image size
+ inc(ptr,4);
+ if flag=0 then
+ flag:=GetImageType(pByte(ptr));
+ FastAnsiToWideBuf(PAnsiChar(@flag),pWideChar(@data64));
+ Info.cover:=SaveTemporaryW(ptr,id,PWideChar(@data64));
+ end;
+ end;
+ FreeMem(buf);
+ end
+ else
+ Skip(f,size);
+ end
+ else
+ begin
+ if (hdr.blocktype and $80)<>0 then
+ break;
+ Skip(f,size);
+ end;
+ end;
+ until (hdr.blocktype and $80)<>0;
+ end;
+ ReadID3v1(f,Info);
+ CloseHandle(f);
+ result:=true;
+end;
+
+var
+ LocalFormatLinkOGG,
+ LocalFormatLinkOGA,
+ LocalFormatLinkOGM,
+ LocalFormatLinkSPX,
+ LocalFormatLinkFLA,
+ LocalFormatLinkFLAC:twFormat;
+
+procedure InitLink;
+begin
+ LocalFormatLinkOGG.Next:=FormatLink;
+
+ LocalFormatLinkOGG.This.proc :=@ReadOGG;
+ LocalFormatLinkOGG.This.ext :='OGG';
+ LocalFormatLinkOGG.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkOGG;
+
+ LocalFormatLinkOGA.Next:=FormatLink;
+
+ LocalFormatLinkOGA.This.proc :=@ReadOGG;
+ LocalFormatLinkOGA.This.ext :='OGA';
+ LocalFormatLinkOGA.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkOGA;
+
+ LocalFormatLinkOGM.Next:=FormatLink;
+
+ LocalFormatLinkOGM.This.proc :=@ReadOGG;
+ LocalFormatLinkOGM.This.ext :='OGM';
+ LocalFormatLinkOGM.This.flags:=WAT_OPT_VIDEO;
+
+ FormatLink:=@LocalFormatLinkOGM;
+
+ LocalFormatLinkSPX.Next:=FormatLink;
+
+ LocalFormatLinkSPX.This.proc :=@ReadSPX;
+ LocalFormatLinkSPX.This.ext :='SPX';
+ LocalFormatLinkSPX.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkSPX;
+
+ LocalFormatLinkFLA.Next:=FormatLink;
+
+ LocalFormatLinkFLA.This.proc :=@ReadfLaC;
+ LocalFormatLinkFLA.This.ext :='FLA';
+ LocalFormatLinkFLA.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkFLA;
+
+ LocalFormatLinkFLAC.Next:=FormatLink;
+
+ LocalFormatLinkFLAC.This.proc :=@ReadfLaC;
+ LocalFormatLinkFLAC.This.ext :='FLAC';
+ LocalFormatLinkFLAC.This.flags:=0;
+
+ FormatLink:=@LocalFormatLinkFLAC;
+end;
+
+initialization
+ InitLink;
+end.
|