unit doggparser;

interface

uses
  dstream,
  DEng;

const
  OGG_BLOCK_SIZE = 64 * 1024;
  OGG_BLOCKS_COUNT = 4;

type
PVorbisInfo = ^TVorbisInfo;
TVorbisInfo = packed record
  version         : LongInt;
  channels        : LongInt;
  rate            : LongInt;
  bitrate_upper   : LongInt;
  bitrate_nominal : LongInt;
  bitrate_lower   : LongInt;
  bitrate_window  : LongInt;
  codec_setup     : Pointer;
end;

TOggSyncState = packed record
  Data: Pointer;
  Storage: LongInt;
  Fill: LongInt;
  Returned: LongInt;
  Unsynced: LongInt;
  HeaderBytes: LongInt;
  BodyBytes: LongInt;
end;

POggVorbisFile = ^TOggVorbisFile;
TOggVorbisFile = record
  {datasource: Pointer;
  seekable: LongInt;
  offset: Int64;
  _end: Int64;
  oy: toggSyncState;
  links: LongInt;
  offsets: PInt64;
  dataoffsets: PInt64;
  serialnos: PInteger;
  pcmlengths: Int64;}
  some: array[0..17] of LongInt;
  vi: PVorbisInfo;
  // переписывать описание всего остального - наказание >_< Благо, это не нужно
  other: array[0..700] of Byte;
end;

TOVCallbacks = record
  read_func  : function(var ptr; size: SizeInt; nmemb: SizeInt; DataSource: Pointer): SizeInt; cdecl;
  seek_func: function (Datasource: Pointer; offset: Int64; whence: LongInt): LongInt; cdecl;
  close_func: function (Datasource: Pointer): LongInt; cdecl;
  tell_func: function (Datasource: Pointer): LongInt; cdecl;
end;  

var
  OVCallbacks: TOVCallbacks;

  ov_clear: function(OggFile: Pointer): LongInt; cdecl; {external VorbisfileLib;}
//  function ov_open_callbacks(Datasource: Pointer; OggFile: Pointer; initial: PChar; ibytes: LongInt; callbacks: TOVCallbacks): LongInt; cdecl; external VorbisFileLib;
  ov_open_callbacks: function(DataSource: Pointer; OggFile: Pointer; initial: PChar; ibytes: LongInt; callbacks: TOVCallbacks): LongInt; cdecl; (* external VorbisFileLib; *)
  ov_pcm_seek: function(OggFile: Pointer; pos: Int64): LongInt; cdecl; (* external VorbisFileLib; *)
  ov_read: function(OggFile: Pointer; Buffer: Pointer; length: LongInt; bigendianp: LongInt; word: LongInt; sgned: LongInt; bitstream: Pointer): LongInt; cdecl; (* external VorbisFileLib; *)

implementation

uses
  dynlibs;

const
  VorbisfileLib = 'vorbisfile.dll';

  SEEK_SET      = 0;
  SEEK_CUR      = 1;
  SEEK_END      = 2;

var
  VorbisHandle: TLibHandle;

function read_func(var ptr; size, nmemb: SizeInt; DataSource: Pointer): SizeInt; cdecl;
begin
  Result := PStream(DataSource).Size - PStream(DataSource).Position;
  if Result > Size * Nmemb then
    Result := Size * Nmemb;
  if Result <= PStream(Datasource).Read(@Ptr, Result) then begin
    Result := Int64(Result) div Int64(Size);
  end else begin
    Result := 0;
  end;
end;

function seek_func(Datasource: Pointer; Offset: Int64; whence: LongInt): LongInt; cdecl;
begin
  case whence of
    SEEK_SET: Result := PStream(DataSource).Seek(Offset, SO_BEGINNING);
    SEEK_CUR: Result := PStream(DataSource).Seek(Offset, SO_CURRENT);
    SEEK_END: Result := PStream(DataSource).Seek(Offset, SO_END);
  else
    Result := 0;
  end;
end;

function close_func(DataSource: Pointer): LongInt; cdecl;
begin
  Result := 0;
end;

function tell_func(DataSource: Pointer): LongInt; cdecl;
begin
  Result := PStream(DataSource).Position;
end;

initialization
  OVCallbacks.read_func := read_func;
  OVCallbacks.seek_func := seek_func;
  OVCallbacks.tell_func := tell_func;
  OVCallbacks.close_func := close_func;

  VorbisHandle := LoadLibrary(VorbisfileLib);
  if VorbisHandle <> NilHandle then begin
    ov_clear := GetProcAddress(VorbisHandle, 'ov_clear');
    ov_open_callbacks := GetProcAddress(VorbisHandle, 'ov_open_callbacks');
    ov_pcm_seek := GetProcAddress(VorbisHandle, 'ov_pcm_seek');
    ov_read := GetProcAddress(VorbisHandle, 'ov_read');
  end;
finalization
  if VorbisHandle <> NilHandle then
    FreeLibrary(VorbisHandle);
end.
